home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mfs055 / minixfs.c < prev    next >
C/C++ Source or Header  |  1992-04-14  |  74KB  |  3,012 lines

  1. /* This is an attempt to add minix filesystem support to MiNT ... use
  2.  * at your own risk!
  3.  */
  4.  
  5. /* 'Minixfs' Copyright S.N.Henson. See 'copying' file for more info.
  6.  *  Version 0.55.
  7.  *  Revision history .....
  8.  *  0.0   Single-partition read-only version with no filename conversion.
  9.  *  0.1   Added multi-partition support and filename conversion for TOS
  10.  *      domain ... added cache.
  11.  *  0.2   Added write support 90% of TOS functions emulated. No write
  12.  *      cache yet ... and no access checking.
  13.  *  0.25  Fixed m_seek bug.
  14.  *  0.3   Added write cache and remaining tos functions fixed some bugs
  15.  *      when long files were read/written. Made search_dir more
  16.  *      tos-like.
  17.  *  0.31  Fixed some bugs . Made it permissible to delete an open file
  18.  *      (just like unix). Added full-disk error checks and minor code
  19.  *       tidy up.
  20.  *  0.32  Added 'zlast' to speed up free zone searches. Also added
  21.  *      consecutive zone read/write to speed up i/o . Fixed bugs
  22.  *      all over the place with curdir .
  23.  *  0.33  Fixed code for > 512 byte sectors. Merged add_zone and
  24.  *      find_zone, new code should support sparse files .
  25.  *  0.34  Tentative support for floppies and removeable media . Tidied up
  26.  *        and removed redundant code (like minix_init() ) .
  27.  *  0.35  Added access checking to all relevant functions.
  28.  *  0.40  Added some un*x like inode related functions , fixed up m_rename
  29.  *      to be more robust (and related things in find_path). Fixed up
  30.  *      m_creat and merged zap_inode() with new function trunc_inode().
  31.  *        Not to mention fixing the occasional bug (or two).
  32.  *  0.41  Cleared up a few bugs , separated system and user caches.
  33.  *  0.50  Changed stuff all over the place for new fs code in 0.9 and
  34.  *      addition of file sharing code, hard and symbolic links (yeah!).
  35.  *  0.51  New compilation option TOS_LWR and tosify filenames from readdir
  36.  *      only if in compatability mode (and all domains).
  37.  *  0.511 Nasty bug fixed in find_zone which screwed files bigger than 1Mb.
  38.  *  0.52  Added V2 filesystem support . Extended RWABS now used , as required.
  39.  *      Code changed all over the place , new alerts added if some problems
  40.  *      are noticed in the filesystem.
  41.  *  0.53  Added code to handle large directory entries.
  42.  *  0.54  Overhaul of tosify code , translation now selectable by fscntl.
  43.  *  0.541 Fixed bug which crashed v2 filesystems.
  44.  *  0.55  Added ROBUST compilation option and file locking.
  45.  *  .........................................................................
  46.  *  Note : 0.31 was distributed with MiNT 0.7
  47.  *       0.40 with MiNT 0.8
  48.  *
  49.  *  .........................................................................  
  50.  *  TODO : (No idea when, and in no particular order)
  51.  *  Add some more Un*x features , directory setu/gid , 
  52.  *        stickies and compaction
  53.  *  Make routines more robust to disk i/o errors.
  54.  *  Tidy up code !
  55.  *  Support for special files .
  56.  *  Dare I mention it .... Make it multi-thread .
  57.  */
  58.  
  59. #include <sys/types.h>
  60. #include <string.h>
  61. #include <ctype.h>
  62.  
  63. #include "atarierr.h"
  64. #include "filesys.h"
  65.  
  66. #include "minixfs.h"
  67. #include "mfsproto.h"
  68.  
  69. #define DOM_TOS 0
  70. #define DOM_MINT 1
  71. #define NUM_DRIVES 32
  72.  
  73. #ifndef NULL
  74. #define NULL 0L
  75. #endif
  76.  
  77. /* Useful macro , is non zero only if 'x' is not a power of two */
  78.  
  79. #define NPOW2(x) ( ( (x) & (x-1) )!=0)
  80.  
  81. /* Dates smaller than 1st Jan 1980 confuse dostime _corr avoids them */
  82.  
  83. #define _corr(t) ( (t > 315532800L) ? t : 315532800L)
  84.  
  85. FILESYS minix_filesys = {
  86.     (FILESYS *) 0,
  87.     FS_CASESENSITIVE,
  88.     m_root, m_lookup, m_creat, m_getdev,
  89.     m_getxattr, m_chattr, m_chown,
  90.     m_chmode, m_mkdir,
  91.     m_rmdir, m_remove, m_getname,
  92.     m_rename, m_opendir, m_readdir,
  93.     m_rewinddir, m_closedir,
  94.     m_pathconf, m_dfree,
  95.     m_wlabel, m_rlabel,
  96.     m_symlink, m_readlink,
  97.     m_hardlink, m_fscntl, m_dskchng
  98. };
  99.  
  100. DEVDRV minix_dev = {
  101.     m_open, m_write, m_read,
  102.     m_seek, m_ioctl, m_datime,
  103.     m_close,m_select, m_unselect
  104. };
  105.  
  106. extern FILESYS tos_filesys;
  107. static bufr temp;
  108.  
  109. super_info *super_ptr[NUM_DRIVES];   /* Pointer to super block for each drive */
  110. #define MINIXDRIVE(i) (super_ptr[i] != 0)
  111.  
  112. char d_check=1;            /* Enable / disable device checking */
  113. char lrecno;            /* set to '1' if RWABS permits 'lrecno' parameter */
  114. char checklrecno;        /* Set to 1 if lrecno checked for */
  115. char lockok;            /* Set to 1 if locking calls allowed */
  116. FILEPTR *firstptr;        /* First FILEPTR in chained list */
  117. long trans_mode=TRANS_DEFAULT;    /* Translation mode */
  118.  
  119. /* mini-cache for m_getname */
  120.  
  121. char *lpath;
  122. fcookie lroot,ldir;
  123.  
  124.  
  125. super_info sblks[NUM_MINIX];
  126.  
  127. cache_control syscache,usrcache;
  128.  
  129. /*
  130.  * this points to the structure that has all the useful functions that
  131.  * the kernel told us about
  132.  */
  133.  
  134. struct kerinfo *kernel;
  135.  
  136. #define CCONWS (void)(*kernel->dos_tab[0x09])
  137. #define RWABS (*kernel->bios_tab[4])
  138. #define GETBPB (void *)(*kernel->bios_tab[7])
  139. #define Timestamp (*kernel->dos_tab[0x2c])
  140. #define Datestamp (*kernel->dos_tab[0x2a])
  141. #define Domain() (*kernel->dos_tab[0x119])(-1)
  142. #define Getpid (*kernel->dos_tab[0x10b])
  143. #define Getuid (*kernel->dos_tab[0x10f])
  144. #define Getgid (*kernel->dos_tab[0x114])
  145.  
  146. #define DEBUG (*kernel->debug)
  147. #define ALERT (*kernel->alert)
  148. #define TRACE (*kernel->trace)
  149. #define FATAL (*kernel->fatal)
  150. #define Kmalloc (*kernel->kmalloc)
  151. #define Kfree (*kernel->kfree)
  152. #define Stricmp (*kernel->stricmp)
  153. #define Strlwr  (*kernel->strlwr)
  154. #define Unixtime (*kernel->unixtim)
  155. #define Dostime (*kernel->dostim)
  156. #define Denyshare (*kernel->denyshare)
  157. #define Denylock (*kernel->denylock)
  158. #define Changedrive (*kernel->drvchng)
  159.  
  160. #ifdef NDEBUG
  161. #define assert(expression)
  162. #else
  163. # ifdef __STDC__
  164. #define assert(expression) \
  165.     ((expression) ? 0 : FATAL("assert(`%s') failed at line %ld of %s.", \
  166.         #expression, (long)__LINE__, __FILE__))
  167. # else
  168. #define assert(expression) if(expression) FATAL("assert(%s) failed", \
  169.         "expression")
  170. # endif
  171. #endif
  172.  
  173. /*
  174.  * this must be the first function; it is called by the kernel when the
  175.  * file system is being loaded, and should return the file system
  176.  * structure
  177.  */
  178.  
  179. FILESYS *
  180. minix_init(k)
  181.     struct kerinfo *k;
  182. {
  183.     kernel = k;
  184.     CCONWS("Minix file system driver for MiNT. Version 0.55 Copyright \
  185. 1991,1992 S.N.Henson\r\n");
  186.     if( (k->maj_version > 0) || (k->min_version >= 94) ) lockok=1;
  187.     else CCONWS("File Locking not Installed , Use MiNT 0.94 or newer\r\n");
  188.  
  189.     return &minix_filesys;
  190. }
  191.  
  192. int
  193. minix_sanity(drv)
  194. int drv;
  195. {
  196.     int i;
  197.     d_inode rip;
  198.     int *bpb;
  199.     int scsiz;
  200.     super_info *psblk;
  201.     for(i=0;i<NUM_MINIX && sblks[i].sblk.s_ninodes;i++)
  202.         ;
  203.     if(i==NUM_MINIX) {
  204.         DEBUG("No room for more Minix Partitions");
  205.         return(0);
  206.     }
  207.     psblk=&sblks[i];
  208.  
  209.     bpb=GETBPB(drv);
  210.     if (!bpb) {
  211.         DEBUG("Getbpb failed!");
  212.         return(0);
  213.     }
  214.     scsiz=bpb[0]/512;
  215.     if(bpb[0]%512 || (scsiz!=1 && scsiz!=2)){
  216.         DEBUG("Partition %c skipped : unsupported sector size",drv+'A');
  217.         return(0);
  218.     }
  219.  
  220.     (void)RWABS(2,&temp,2/scsiz,(SUPER_BLOCK<<1)/scsiz,drv);
  221.     psblk->sblk=*((super_block *)&temp);
  222.  
  223.     if(( (psblk->sblk.s_magic==SUPER_MAGIC) || 
  224.          (psblk->sblk.s_magic==SUPER_V2))
  225.         && psblk->sblk.s_ninodes) {
  226.  
  227.         if(psblk->sblk.s_log_zsize) {
  228.         DEBUG("Cannot read partition %c Zone-size>Block-size",drv+'A');
  229.             psblk->sblk.s_ninodes=0;
  230.             return 0;
  231.         }
  232.         if(psblk->sblk.s_magic==SUPER_MAGIC)
  233.         {
  234.             TRACE("Drive %c V1 filesyetem",drv+'A');
  235.             psblk->version=0;
  236.             psblk->ipb=INODES_PER_BLOCK;
  237.             psblk->zpind=NR_INDIRECTS;
  238.             psblk->sblk.s_zones=psblk->sblk.s_nzones;
  239.             psblk->dzpi=NR_DZONE_NUM;
  240.             psblk->ndbl=NR_DBL;
  241.         }
  242.         else
  243.         {
  244.             TRACE("Drive %c V2 filesystem",drv+'A');
  245.             psblk->version=1;
  246.             psblk->ipb=INODES_PER_BLOCK2;
  247.             psblk->zpind=NR_INDIRECTS2;
  248.             psblk->dzpi=NR_DZONE_NUM2;
  249.             psblk->ndbl=NR_DBL2;
  250.  
  251.         }
  252.  
  253.         /* Does the partition need lrecno ? */
  254. #ifdef LRDEBUG
  255.         if(drv > 1 )
  256. #else
  257.         if( ( (psblk->sblk.s_zones > 32767) && scsiz==1) 
  258.             || psblk->sblk.s_zones > 65534 ) 
  259. #endif
  260.         {
  261.             if(!lrecno)
  262.             {
  263.                 if(checklrecno || nolrecno(temp.bdata,scsiz,drv) )
  264.                 {
  265.                     DEBUG("Partition %c too big",drv+'A');
  266.                     psblk->sblk.s_ninodes=0;
  267.                     return 0;
  268.                 }
  269.             }
  270.             psblk->big=1;
  271.         }
  272.         else psblk->big=0;
  273.  
  274.         super_ptr[drv]=psblk;    /* Point drive at super block */
  275.         psblk->ioff = psblk->sblk.s_imap_blks + psblk->sblk.s_zmap_blks
  276.                                      + 2 ;
  277.         psblk->scsiz=scsiz;
  278.  
  279.         /* If we get this far allocate a cache if non-present */
  280.         if(!syscache.start && init_cache()) {
  281.             DEBUG("No room for Cache");
  282.             return(0);
  283.         }
  284.  
  285.         read_inode(ROOT_INODE,&rip,drv);
  286.         if(IS_DIR(rip)) {
  287.           void *p;
  288.           int dot=-1,dotdot=-1;
  289.           p=Kmalloc((unsigned) (BLOCK_SIZE*(psblk->sblk.s_imap_blks+
  290.             psblk->sblk.s_zmap_blks)));
  291.           if(!p) {
  292.             DEBUG("No room for bitmaps");
  293.             return(0);
  294.           }
  295.           psblk->ibitmap=p;
  296.           psblk->zbitmap=p+BLOCK_SIZE*psblk->sblk.s_imap_blks;
  297.           crwabs(2,p,psblk->sblk.s_imap_blks+psblk->sblk.s_zmap_blks
  298.                                     ,2,drv);
  299.           psblk->idirty=0;
  300.           psblk->zdirty=0;
  301.           psblk->zlast=0;
  302.           psblk->dev=drv;
  303.  
  304. /* Final step , read in the root directory zone 1 and check the '.' and '..'
  305.  * spacing , The spacing between the '.' and '..' will be used as an indicator
  306.  * of the directory entry size. If in doubt assume a normal minix filesystem.
  307.  */
  308.  
  309.           read_zone(rip.i_zone[0],&temp,drv,&syscache);
  310.  
  311.           for(i=0;i<min(NR_DIR_ENTRIES,rip.i_size/DIR_ENTRY_SIZE);i++)
  312.           {
  313.             if(temp.bdir[i].d_inum)
  314.             {
  315.                 if(!strcmp(temp.bdir[i].d_name,"."))
  316.                 {
  317.                     if(dot==-1) dot=i;
  318.                     else
  319.                     {
  320.                         ALERT("Drive %c multiple ""."" in root dir!!",drv+'A');
  321.                         dot=-1;
  322.                         i=NR_DIR_ENTRIES;
  323.                     }
  324.                 }
  325.  
  326.                 if(!strcmp(temp.bdir[i].d_name,".."))
  327.                 {
  328.                     if(dotdot==-1) dotdot=i;
  329.                     else
  330.                     {
  331.                         ALERT("Drive %c multiple "".."" in root directory",drv+'A');
  332.                         dotdot=-1;
  333.                         i=NR_DIR_ENTRIES;
  334.                     }
  335.                 }
  336.             }
  337.           }
  338.  
  339.           if( (dotdot==-1) || (dot==-1) )
  340.           {
  341.             ALERT("Drive %c no . or .. in root directory",drv+'A');
  342.             psblk->increment=1;
  343.           }
  344.           else psblk->increment=dotdot-dot;
  345.  
  346.           if( (psblk->increment < 1) 
  347.           || NPOW2(psblk->increment)
  348.           || (psblk->increment > MAX_INCREMENT ))
  349.           {
  350.             ALERT("Drive %c weird . .. positions",drv+'A');
  351.             psblk->increment=1;
  352.           }
  353. #ifdef BIGDIRTEST
  354.           ALERT("Drive %c increment %d",drv+'A',psblk->increment);
  355. #endif
  356.           return(1);
  357.         }
  358.         else {
  359.             DEBUG("root inode on drive %c is not a directory??",
  360.                 drv+'A');
  361.         }
  362.     }
  363.     psblk->sblk.s_ninodes=0;
  364.     return(0);
  365. }
  366.  
  367. /* This function returns '1' if the RWABS present does not support the extra
  368.  * long parameter used for large partitions or if the drive is floppy , it
  369.  * sets 'checklrecno' if this test has been performed, and 'lrecno' if this
  370.  * extra parameter is supported , what it does is to read in the super blocks
  371.  * block using the extra parameter and checks it with the one already read.
  372.  */
  373.  
  374. int nolrecno(cmptest2,scsiz,drv)
  375. char *cmptest2;
  376. int scsiz;
  377. int drv;
  378. {
  379.     char *cmptest;
  380.     if(drv < 2) return 1;
  381.     if(cmptest=Kmalloc(BLOCK_SIZE))
  382.     {
  383.         if(!RWABS(2,cmptest,2/scsiz,-1,drv,
  384.                     (long)((SUPER_BLOCK<<1)/scsiz)))
  385.         {
  386.             if(!bcmp(cmptest,cmptest2,BLOCK_SIZE))
  387.             {    
  388.                 lrecno=1;
  389. #ifdef LRDEBUG
  390.                 ALERT("lrecno supported");
  391. #else
  392.                 TRACE("lrecno supported");
  393. #endif
  394.             }
  395.         }
  396.         Kfree(cmptest);
  397.     }
  398.     else DEBUG("minixfs nolrecno:Kmalloc failed");
  399.     checklrecno++;
  400.     if(lrecno) return 0;
  401.     return 1;
  402. }
  403.  
  404.  
  405. /*
  406.  * the kernel calls this when it detects a disk change
  407.  */
  408.  
  409. static long m_dskchng(d)
  410. int d;
  411. {
  412.     TRACE("Disk Change drive %c",d+'A');
  413.     
  414.     Kfree(super_ptr[d]->ibitmap);
  415.     super_ptr[d]->sblk.s_ninodes=0;
  416.  
  417. /* this may affect the m_getname cache, too */
  418.     if (lpath && (d == lroot.dev || d == ldir.dev)) {
  419.         Kfree(lpath);
  420.         lpath = 0;
  421.     }
  422.  
  423. /* Since the disk has changed always invalidate cache */
  424.     m_invalidate(d);
  425.  
  426. /* If new drive not minix update super block pointer */
  427.     if (!minix_sanity(d)) super_ptr[d] = 0;
  428.  
  429.     return 1;
  430. }
  431.  
  432. /* Invalidate all cache entries for a given drive */
  433.  
  434. void m_invalidate(drv)
  435. int drv;
  436. {
  437.     char warned=0;
  438.     cache *p;
  439.     for(p=syscache.start;p!=usrcache.end;p++)
  440.     if(p->drive==drv && p->status)
  441.     {
  442.         if(p->status==2 && !warned++)
  443.         ALERT("Cache entry not written out when drive %c invalidated",drv+'A');
  444.         p->status=0;
  445.     }
  446. }
  447.  
  448.  
  449.  
  450.  
  451. /* Zone and inode number range checkers , print out 'mesg' and the 
  452.  * drive,zone if invalid
  453.  */
  454.  
  455. int chk_zone(num,drv,mesg)
  456. long num;
  457. int drv;
  458. char *mesg;
  459. {
  460.     super_info *psblk=super_ptr[drv];
  461.     if(!num) return 0;    /* special for zeroed out block */
  462.     if(num < psblk->sblk.s_firstdatazn || num >= psblk->sblk.s_zones)
  463.     {
  464.         ALERT("%s : Illegal zone , drive %c zone %ld",mesg,drv+'A',num);
  465.         return 1;
  466.     }
  467.     return 0;
  468. }
  469.  
  470. int chk_inode(num,psblk,mesg)
  471. super_info *psblk;
  472. long num;
  473. char *mesg;
  474. {
  475.     if(num < 1 || num > psblk->sblk.s_ninodes)
  476.     {
  477.         ALERT("%s : Illegal inode number , drive %c inode %ld",mesg,
  478.             psblk->dev+'A',num);
  479.         return 1;
  480.     }
  481.     return 0;
  482. }
  483.  
  484. /* This is a 'corrected rwabs' , it behaves as though sector-size==1K
  485.  * irrespective of what it really is. Currently it only supports 512 byte 
  486.  * and 1K sectors , also if the extended rwabs is needed use it .
  487.  */
  488.  
  489. void crwabs(rw,buf,num,recno,dev)
  490. int rw;
  491. void *buf;
  492. unsigned num;
  493. long recno;
  494. int dev;
  495. {
  496.     long r;
  497.     super_info *psblk=super_ptr[dev];
  498.  
  499.     /* If something *really* bad happened forbid overwrite of
  500.           * boot sector and super block
  501.       */
  502.  
  503.     if(rw && recno < 2) 
  504.     {
  505.         ALERT("Crwabs, Illegal Write: drive %c sector %ld",dev+'A',recno);
  506.         return ;
  507.     }
  508.  
  509.     if(d_check && !MINIXDRIVE(dev)) {
  510.         ALERT("Minixfs : attempted I/O with illegal partition %d!",
  511.             dev);
  512.         ALERT("Minixfs : please submit a bug report");
  513.         return ;
  514.     }
  515.  
  516.     if(psblk->scsiz==1)
  517.     {
  518.         if(psblk->big) r = RWABS(rw,buf,num<<1,-1,dev,recno<<1);
  519.         else r = RWABS(rw,buf,num<<1,(unsigned)(recno<<1),dev);
  520.     }
  521.     else
  522.     {
  523.         if(psblk->big) r = RWABS(rw,buf,num<<1,-1,dev,recno);
  524.         r = RWABS(rw,buf,num,(unsigned)recno,dev);
  525.     }
  526.  
  527.     if (r) ALERT("minixfs: Rwabs returned %ld block %ld drive %d"
  528.                 , r,recno,dev);
  529.  
  530.     return ;
  531. }
  532.  
  533. /* For this kind of file system a cache is absolutely essential ,
  534. without it your hard-disk will sound like a buzz-saw .... the 
  535. idea is fairly simple , for every block requested if it is not in 
  536. the cache load it in at the current position .Then return a 
  537. pointer to the block . Additionally all writes go to the cache if
  538. an entry exists for the specified block . This means that when a 
  539. cache entry is overwritten we must write out the entry if it is
  540. 'dirty' , the function l_sync() writes out the entire cache along
  541. with the zone and inode bitmaps. All functions , except write , call
  542. l_sync() on each call . Write calls l_sync() only after a certain
  543. amount of data has been written , this stops lots of little writes
  544. taking too much time .... However for an increase in performance , 
  545. two caches are used , one for inodes and directories , one for
  546. files , these are kmalloc()'ed at the same time so that one follows the
  547. other in memory , this simplifies cache-flushing for example.
  548. */
  549.  
  550. /* Initialise cache */
  551. int init_cache()
  552. {
  553.     cache *ctemp;
  554.         /* Start with system cache */
  555.     if(!(ctemp=Kmalloc((SCACHE_SIZE+UCACHE_SIZE)*SIZEOF(cache))))
  556.         return 1;
  557.     syscache.start=ctemp;
  558.     syscache.end=&ctemp[SCACHE_SIZE];
  559.     syscache.pos=syscache.start;
  560.     usrcache.start=syscache.end;
  561.     usrcache.pos=usrcache.start;
  562.     usrcache.end=&usrcache.start[UCACHE_SIZE];
  563.     /* Invalidate all entries */
  564.     for(ctemp=syscache.start;ctemp!=usrcache.end;ctemp++)
  565.         ctemp->status=0;
  566.  
  567.     return 0;
  568. }
  569.  
  570.  
  571. void
  572. l_sync()
  573. {
  574.     cache *p;
  575.     int i;
  576. /* Write out dirty cache entries */ 
  577.     for(p=syscache.start;p!=usrcache.end;p++)
  578.     {
  579.     if(p->status==2)
  580.     {
  581.         crwabs(3,&p->buffer,1,p->block,p->drive);
  582.         p->status=1;
  583.     }
  584.     }
  585.     /* Now inode and zone bitmaps */
  586.     for(i=0;i<NUM_DRIVES;i++)
  587.     {
  588.     if(MINIXDRIVE(i)) {
  589.         super_info *psblk;
  590.         psblk=super_ptr[i];
  591.         if(psblk->idirty)
  592.             crwabs(3,psblk->ibitmap,psblk->sblk.s_imap_blks,2,i);
  593.          if(psblk->zdirty)
  594.             crwabs(3,psblk->zbitmap,psblk->sblk.s_zmap_blks,
  595.                 psblk->sblk.s_imap_blks+2,i);
  596.         psblk->idirty=0;
  597.         psblk->zdirty=0;
  598.       }
  599.     }
  600. }
  601.  
  602. /* Return cache entry for numr,drive if it exists or NULL */
  603.  
  604. cache *in_cache(numr,drive,control)
  605. long numr;
  606. int drive;
  607. cache_control *control;
  608. {
  609.     cache *p;
  610.     for(p=control->start;p!=control->end;p++)
  611.     if((p->status) && (p->block==numr) && (p->drive==drive))
  612.         return(p);
  613.     return NULL;
  614. }
  615.  
  616. /* Return a pointer to block numr,drive loading and updating cache if needed */
  617.  
  618. bufr *cget_block(numr,drive,control)
  619. long numr;
  620. int drive;
  621. cache_control * control;
  622. {
  623.     return(&(cache_get(numr,drive,control)->buffer));
  624. }
  625.  
  626. cache *
  627. cache_get(numr,drive,control)
  628. long numr;
  629. int drive;
  630. cache_control *control;
  631. {
  632.     cache *p;
  633.     if(p=in_cache(numr,drive,control))return(p);
  634.     /* Read block in */
  635.     if(control->pos==control->end)
  636.         control->pos=control->start;
  637.     /* Write out dirty entries before they are overwritten */
  638.     if(control->pos->status==2)
  639.         crwabs(3,&control->pos->buffer,1,control->pos->block,
  640.         control->pos->drive);
  641.  
  642.     crwabs(2,&control->pos->buffer,1,numr,drive);
  643.     /* Update Cache */
  644.     control->pos->drive=drive;
  645.     control->pos->block=numr;
  646.     control->pos->status=1;
  647.     return(control->pos++);
  648. }
  649.  
  650. /* Write out block, search cache and if 'hit' update and mark as dirty 
  651.  * otherwise just write out block .
  652.  */
  653.  
  654. int
  655. cput_block(numr,drive,buf,control)
  656. long numr;
  657. int drive;
  658. void *buf;
  659. cache_control *control;
  660. {
  661.     cache *p;
  662.  
  663.     if(p=in_cache(numr,drive,control)){
  664.         if(buf!=&p->buffer) bcopy(buf,&p->buffer,BLOCK_SIZE);
  665.         p->status=2;
  666.         return 0;
  667.     }
  668.     crwabs(3,buf,1,numr,drive);
  669.     return 0;
  670. }
  671.  
  672.  
  673. int read_inode(num,rip,drv)
  674. unsigned num;
  675. d_inode *rip;
  676. int drv;
  677. {
  678.     bufr *tmp;
  679.     super_info *psblk=super_ptr[drv];
  680.     if(chk_inode(num,psblk,"read_inode")) return 1;
  681.     num-=1;
  682.     tmp=cget_block(num/psblk->ipb+psblk->ioff,drv,&syscache);
  683.     if(psblk->version) *rip=tmp->binode[num%psblk->ipb];
  684.     else
  685.     {
  686.         d_inode1 *oldrip;
  687.         int i;
  688.         oldrip=&tmp->binode1[num%psblk->ipb];
  689.         /* Convert V1 inode to V2 */
  690.         rip->i_mode=oldrip->i_mode;
  691.         rip->i_nlinks=oldrip->i_nlinks;
  692.         rip->i_uid=oldrip->i_uid;
  693.         rip->i_gid=oldrip->i_gid;
  694.         rip->i_size=oldrip->i_size;
  695.         rip->i_atime=oldrip->i_mtime;
  696.         rip->i_mtime=oldrip->i_mtime;
  697.         rip->i_ctime=oldrip->i_mtime;
  698.         for(i=0;i< NR_ZONE_NUMS;i++) rip->i_zone[i]=oldrip->i_zone[i];
  699.         rip->i_zone[NR_ZONE_NUMS]=0;
  700.     }
  701.     return(0);
  702. }
  703.  
  704. int write_inode(num,rip,drv)
  705. unsigned num;
  706. d_inode *rip;
  707. int drv;
  708. {
  709.     cache *tmp;
  710.     super_info *psblk=super_ptr[drv];
  711.     if(chk_inode(num,psblk,"write_inode")) return 1;
  712.     num-=1;
  713.     tmp=cache_get(num/psblk->ipb+psblk->ioff,drv,&syscache);
  714.     if(psblk->version) tmp->buffer.binode[num%psblk->ipb]=*rip;
  715.     else
  716.     {
  717.         d_inode1 *oldrip;
  718.         int i;
  719.         oldrip=&tmp->buffer.binode1[num%psblk->ipb];
  720.         /* Convert V2 inode to V1 */
  721.         oldrip->i_mode=rip->i_mode;
  722.         oldrip->i_nlinks=rip->i_nlinks;
  723.         oldrip->i_uid=rip->i_uid;
  724.         oldrip->i_gid=rip->i_gid;
  725.         oldrip->i_size=rip->i_size;
  726.         oldrip->i_mtime=rip->i_mtime;
  727.         for(i=0;i<NR_ZONE_NUMS;i++) oldrip->i_zone[i]=rip->i_zone[i];
  728.     }
  729.     tmp->status=2;
  730.     return(0);
  731. }
  732.  
  733. int read_zone(num,buf,drive,control)
  734. long num ;
  735. void *buf;
  736. int drive;
  737. cache_control *control;
  738. {
  739.     if(chk_zone(num,drive,"read_zone")) return 1;
  740.     if(num) bcopy(cget_block(num,drive,control),buf,(size_t)BLOCK_SIZE);
  741.     else bzero(buf,(size_t)BLOCK_SIZE);
  742.     return(0);
  743. }
  744.  
  745. /* Only ever used for directories so always syscache */
  746. bufr *get_zone(num,drive)
  747. long num ;
  748. int drive;
  749. {
  750.     if(chk_zone(num,drive,"get_zone")) FATAL("Halted");
  751.     return(cget_block(num,drive,&syscache));
  752. }
  753.  
  754.  
  755. int write_zone(num,buf,drive,control)
  756. long num ;
  757. void *buf;
  758. int drive;
  759. cache_control *control;
  760. {
  761.     if(chk_zone(num,drive,"write_zone")) return 1;
  762.     cput_block(num,drive,buf,control);
  763.     return(0);
  764. }
  765.  
  766. /* This is a 'clever' write_zone which recognises consecutive blocks and
  767.  * queues requests until it gets one out of sequence.This allows large
  768.  * i/o requests to be done with a single Rwabs for consecutive blocks
  769.  * which is much faster than lots of little ones.
  770.  */
  771.  
  772. int write_zones(num,buf,drive,control)
  773. long num;
  774. void *buf;
  775. int drive;
  776. cache_control *control;
  777. {
  778.     static void *qstart,*qnext;
  779.     static zone_nr nstart,nnext;
  780.     static zone_nr count;
  781.     static short qdrive=-1;
  782.     cache *p;
  783.     /* Update cache if necessary */
  784.     if(chk_zone(num,drive,"write_zones")) return 1;
  785.     if(p=in_cache(num,drive,control))
  786.     {
  787.         bcopy(buf,&p->buffer,(size_t)BLOCK_SIZE);
  788.         p->status=1;
  789.     }
  790.     if(buf!=qnext || nnext!=num || qdrive!=drive)/* Flush out queue */
  791.     {
  792.         if(qdrive!=-1)crwabs(3,qstart,count,nstart,qdrive);
  793.         qdrive=drive;
  794.         qstart=buf;
  795.         qnext=buf+BLOCK_SIZE;
  796.         nstart=num;
  797.         nnext=num+1;
  798.         count=1;
  799.     }
  800.     else 
  801.     {
  802.         qnext+=BLOCK_SIZE;
  803.         count++;
  804.         nnext++;
  805.     }
  806.     return 0;
  807. }
  808.  
  809. /* This is an equivalent for read ... but this is a bit harder as it is
  810.  * not obvious what to do with the cache . What we finally do is to
  811.  * always get data from the cache if we can , though this could easily
  812.  * turn a large consecutive i/o request into lots of little ones . The
  813.  * cache is not filled from the new read unless we are only reading
  814.  * 1 zone ... basically this assumes that if the user reads several zones
  815.  * then the program will be doing doing some sort of cacheing itself .
  816.  */
  817.  
  818. int read_zones(num,buf,drive,control)
  819. long num;
  820. void *buf;
  821. int drive;
  822. cache_control *control;
  823. {
  824.     static void *qstart,*qnext;
  825.     static zone_nr nstart,nnext;
  826.     static zone_nr count;
  827.     static short qdrive=-1;
  828.     cache *p;
  829.     if(chk_zone(num,drive,"read_zones")) return 1;
  830.     /* Read from cache if necessary */
  831.     if(p=in_cache(num,drive,control))
  832.     {
  833.         bcopy(&p->buffer,buf,(size_t)BLOCK_SIZE);
  834.         drive=-1; /* Force flush of queued entries */
  835.     }
  836.     if(buf!=qnext || nnext!=num || qdrive!=drive)/* Flush out queue */
  837.     {
  838.         if(qdrive!=-1)
  839.         {
  840.             if(count==1)read_zone(nstart,qstart,qdrive,control);
  841.             else {
  842.                 if(nnext) crwabs(2,qstart,count,
  843.                     nstart,qdrive);
  844.                 else bzero(qstart,count*(size_t)BLOCK_SIZE);
  845.             }
  846.         }
  847.         qdrive=drive;
  848.         qstart=buf;
  849.         qnext=buf;
  850.         nstart=num;
  851.         nnext=num;
  852.         count=0;
  853.     }
  854.     if(qdrive!=-1)
  855.     {
  856.         qnext+=BLOCK_SIZE;
  857.         count++;
  858.         if(nnext)nnext++;
  859.     }
  860.     return 0;
  861. }
  862.  
  863. /* This routine finds the zone 'numr' of an inode , traversing indirect
  864.  * and double indirect zones as required if flag!=0 zones are added as
  865.  * required . Having two filesystem versions makes this a bit trickier ...
  866.  */
  867.  
  868. long find_zone(rip,numr,drive,flag)
  869. d_inode *rip;
  870. long numr;
  871. int drive;
  872. int flag;
  873. {
  874.     long temp_zone,temp_zone2;
  875.     bufr *tmp,*tmp2;
  876.     super_info *psblk=super_ptr[drive];
  877.     char vers;    /* Filesys version */
  878.  
  879.     vers=psblk->version;
  880.  
  881.     /* Past EOF ? */
  882.     if((numr*BLOCK_SIZE >= rip->i_size) && !flag ) return(0);
  883.  
  884.     /* Zone in inode ? */
  885.     if(numr < psblk->dzpi)
  886.     {
  887.         temp_zone=rip->i_zone[numr];
  888.         if(temp_zone || !flag ) return temp_zone;
  889.         return(rip->i_zone[numr]=alloc_zone(drive));
  890.     }
  891.     /* In indirect zone then ? */
  892.     else if(numr < psblk->ndbl)
  893.     {
  894.         if(rip->i_zone[7])
  895.         {
  896.             tmp=get_zone(rip->i_zone[7],drive);
  897.             temp_zone=PIND(vers,tmp,numr-psblk->dzpi);
  898.             if( temp_zone || !flag )return temp_zone;
  899.             temp_zone=alloc_zone(drive);
  900.             if(temp_zone)
  901.             {            
  902.                 PIND(vers,tmp,numr-psblk->dzpi)=temp_zone;
  903.                 write_zone(rip->i_zone[7],tmp,drive,&syscache);
  904.             }
  905.             return temp_zone;
  906.         }
  907.         else
  908.         {
  909.             if(!flag || !(rip->i_zone[7]=alloc_zone(drive))) return 0;
  910.             bzero(&temp,(size_t)BLOCK_SIZE);
  911.             temp_zone=alloc_zone(drive);
  912.             IND(vers,temp,numr-psblk->dzpi)=temp_zone;
  913.             write_zone(rip->i_zone[7],&temp,drive,&syscache);
  914.             return temp_zone;
  915.         }
  916.     }
  917.     else/* Erk double indirect .... */
  918.     {
  919.  
  920.             if(rip->i_zone[8]) {
  921.             tmp=get_zone(rip->i_zone[8],drive);
  922.             temp_zone2=PIND(vers,tmp,(numr-psblk->ndbl)/psblk->zpind);
  923.  
  924.             if(temp_zone2)
  925.             {
  926.                      tmp2=get_zone(temp_zone2,drive);
  927.                 temp_zone=PIND(vers,tmp2,(numr-psblk->ndbl)%psblk->zpind);
  928.                 if(temp_zone || !flag)return temp_zone;
  929.                 if(temp_zone=alloc_zone(drive))
  930.                 {
  931.                     PIND(vers,tmp2,(numr-psblk->ndbl)%psblk->zpind)=temp_zone;
  932.                     write_zone(temp_zone2,tmp2,drive,&syscache);
  933.                 }
  934.                 return temp_zone;
  935.               }
  936.               else 
  937.               {
  938.                      if(!flag ||!(temp_zone2=alloc_zone(drive)) )return 0;
  939.                 PIND(vers,tmp,(numr-psblk->ndbl)/psblk->zpind)=temp_zone2;
  940.                      write_zone(rip->i_zone[8],tmp,drive,&syscache);
  941.                      bzero(&temp,(size_t)BLOCK_SIZE);
  942.                 temp_zone=alloc_zone(drive);
  943.                 IND(vers,temp,(numr-psblk->ndbl)%psblk->zpind)=temp_zone;
  944.                 write_zone(temp_zone2,&temp,drive,&syscache);
  945.                      return temp_zone;
  946.               }
  947.         }
  948.         if(!flag || !(rip->i_zone[8]=alloc_zone(drive)) ) return 0;
  949.  
  950.         bzero(&temp,(size_t)BLOCK_SIZE);
  951.         temp_zone=alloc_zone(drive);
  952.         if(temp_zone)
  953.         {
  954.             IND(vers,temp,(numr-psblk->ndbl)/psblk->zpind)=temp_zone;
  955.             write_zone(rip->i_zone[8],&temp,drive,&syscache);
  956.         }
  957.         else return 0;
  958.         bzero(&temp,(size_t)BLOCK_SIZE);
  959.         temp_zone2=alloc_zone(drive);
  960.         IND(vers,temp,(numr-psblk->ndbl)%psblk->zpind)=temp_zone2;
  961.         write_zone(temp_zone,&temp,drive,&syscache);
  962.         return temp_zone2;
  963.  
  964.     }
  965. }
  966.  
  967.  
  968.  
  969. /* This reads zone number 'numr' of an inode .
  970.  * It returns the actual number of valid characters in 'numr' , this is only
  971.  * used for directories so it is hard-coded for the system cache. 
  972.  */
  973.  
  974. int next_zone(rip,numr,buf,drive)
  975. d_inode *rip;
  976. long numr;
  977. void *buf;
  978. int drive;
  979. {
  980.     long ztemp;
  981.     off_t ret;
  982.  
  983.     ret=min(rip->i_size-numr*BLOCK_SIZE,BLOCK_SIZE);
  984.     if(ret <= 0)return 0;
  985.     ztemp=find_zone(rip,numr,drive,0);
  986.     read_zone(ztemp,buf,drive,&syscache);
  987.     return (int)ret;
  988. }
  989.  
  990. /* As above but reads in cache pointer */
  991.  
  992. int cnext_zone(rip,numr,buf,drive)
  993. d_inode *rip;
  994. long numr;
  995. bufr **buf;
  996. int drive;
  997. {
  998.     long ztemp;
  999.     off_t ret;
  1000.  
  1001.     ret=min(rip->i_size-numr*BLOCK_SIZE,BLOCK_SIZE);
  1002.     if(ret <= 0)return 0;
  1003.     ztemp=find_zone(rip,numr,drive,0);
  1004.     *buf=get_zone(ztemp,drive);
  1005.     return (int)ret;
  1006. }
  1007.  
  1008.  
  1009. /* search_dir serves 3 functions dependent on 'mode'
  1010. 0 Search a directory for the entry 'name' if found return its inode number.
  1011. 1 Create an entry 'name' return position of d_inum .
  1012. 2 Delete an entry 'name' return inode num for success .
  1013. 3 Find entry 'name', return position of d_inum.
  1014. In all cases failure is denoted by a negative error number .
  1015. */
  1016.  
  1017. off_t search_dir(name,inum,drive,flag)
  1018. char *name;
  1019. unsigned inum;
  1020. int drive;
  1021. int flag;
  1022. {
  1023.     int entry,count;
  1024.     long zone;        /* Zone number within dir */
  1025.     off_t lstfree;    /* Last unused dir entry */
  1026.     d_inode rip;
  1027.     bufr *tmp;
  1028.     int tflag;
  1029.     int incr;
  1030.     static char tname[MNAME_MAX];
  1031.  
  1032.     tflag=do_trans(SRCH_TOS);
  1033.  
  1034.     incr=super_ptr[drive]->increment;
  1035.     strcpy(tname,tosify(name,tflag,MMAX_FNAME(incr)));
  1036.     read_inode(inum,&rip,drive);
  1037.     /* Must be a directory ! */
  1038.     if(!IS_DIR(rip)) return EPTHNF;
  1039.  
  1040.     lstfree=-1l;
  1041.     for(zone=0; (count=cnext_zone(&rip,zone,&tmp,drive)/DIR_ENTRY_SIZE);
  1042.     zone++)
  1043.     {
  1044.         for(entry=0;entry<count;entry+=incr) {
  1045.          dir_struct *try=&tmp->bdir[entry];
  1046.              mino_t inumtemp;
  1047.              inumtemp=try->d_inum;
  1048.              if(inumtemp &&
  1049.             (!strncmp(name,try->d_name,MMAX_FNAME(incr)) || (tflag && 
  1050.         (!strcmp(tname,tosify(try->d_name,tflag,MMAX_FNAME(incr))) ) )))
  1051.  
  1052.             {
  1053.                 if(flag==KILL)
  1054.             {
  1055.                   /* Never ever allow unlinking of '.' or '..' */
  1056.                   if(zone==0 && entry<2)return EACCDN;
  1057.                       try->d_inum=0;
  1058.  
  1059.             /* Next bit pinched from Minix,
  1060.              * store old inode num in last 2 bytes of name
  1061.              * This allows recovery in case of accident
  1062.              */
  1063.         *((mino_t *)&try->d_name[MMAX_FNAME(incr)-2])=inumtemp;
  1064.  
  1065.                 write_zone(find_zone(&rip,zone,drive,0),tmp
  1066.                 ,drive,&syscache);
  1067.                 }
  1068.             if(flag==ADD) return  EACCDN;
  1069.         if(flag==FIND || flag==KILL ) return inumtemp;
  1070.             if(flag==POS) return entry*DIR_ENTRY_SIZE+zone*BLOCK_SIZE;
  1071.         }
  1072.             if(lstfree==-1l && !inumtemp)
  1073.             lstfree=zone*BLOCK_SIZE+entry*DIR_ENTRY_SIZE;
  1074.         }
  1075.     }
  1076.  
  1077.     if(flag==ADD) {
  1078.     dir_struct add[MAX_INCREMENT];
  1079.     strncpy(tname,name,MMAX_FNAME(incr) );
  1080.     tname[MMAX_FNAME(incr)]=0;
  1081.     if(badname(tname)) return EACCDN;
  1082.     if( do_trans(LWR_TOS) ) Strlwr(tname);
  1083.           strncpy(add[0].d_name,tname,MMAX_FNAME(incr) );
  1084.     add[0].d_inum=0;
  1085.     if(l_write(inum,lstfree,(off_t)(DIR_ENTRY_SIZE*incr),add,drive)
  1086.             !=(DIR_ENTRY_SIZE*incr) ) return EACCDN;
  1087.     return( lstfree==-1l ? rip.i_size : lstfree);
  1088.     }
  1089.     return EFILNF;
  1090. }
  1091.  
  1092. /* Return '1' is 'name' has any illegal charaters in it */
  1093.  
  1094. int badname(name)
  1095. char *name;
  1096. {
  1097.     for(;*name;name++) if(BADCHR(*name)) 
  1098.     {
  1099.         DEBUG("minixfs: Bad character in filename");
  1100.         return 1;
  1101.     }
  1102.     return 0;
  1103. }
  1104.  
  1105. /* New filesys routines for 0.9 , the 'cookie' for minix filesystems is very
  1106. simple , 'dev' is the Rwabs device number , 'index' is the inode number , in
  1107. addition the aux field is used when a fileptr is unlinked */
  1108.  
  1109. /*
  1110.  * Note: in the first round of initialisations, we assume that floppy
  1111.  * drives (A and B) don't belong to us; but in a later disk change,
  1112.  * they may very well be ours, so we remember that
  1113.  */
  1114.  
  1115. static long m_root(dev,dir)
  1116. int dev;
  1117. fcookie *dir;
  1118. {
  1119.     static first_init = 2;
  1120.     int i;
  1121.     long ret = -1;
  1122.  
  1123.     /* the first 2 checks (on A: and B:) we fail automatically */
  1124.     if (first_init) {
  1125.         --first_init;
  1126.         return ret;
  1127.     }
  1128.  
  1129.     d_check=0;
  1130.         /* Check if drive already recognised */
  1131.     for(i=0;i<NUM_MINIX;i++)
  1132.         if(sblks[i].sblk.s_ninodes &&  sblks[i].dev==dev) break;
  1133.         /* If not present , see if it's valid */
  1134.     if( i < NUM_MINIX || ( dev >= 0 && minix_sanity(dev) ) ) {
  1135.         dir->fs=&minix_filesys;
  1136.         dir->dev=dev;
  1137.         dir->aux=0;
  1138.         dir->index=ROOT_INODE;    /* Always same for minix filesytems */
  1139.         ret=0;
  1140.     }
  1141.     return ret;
  1142. }
  1143.  
  1144. static long m_lookup(dir,name,entry)
  1145. fcookie *dir; 
  1146. char *name;
  1147. fcookie *entry;
  1148. {
  1149.     if(!*name)
  1150.     {
  1151.         *entry=*dir;
  1152.         return 0;
  1153.     }
  1154.     if(dir->index==ROOT_INODE && !strcmp(name,".."))
  1155.     {
  1156.         *entry=*dir;
  1157.         DEBUG("m_lookup returned EMOUNT");
  1158.         return EMOUNT;
  1159.     }
  1160.     entry->index=search_dir(name,dir->index,dir->dev,FIND);
  1161.     if(entry->index < 0 ) return entry->index ;
  1162.     entry->dev=dir->dev;
  1163.     entry->aux=0;
  1164.     entry->fs=&minix_filesys;
  1165.     return 0;
  1166. }
  1167.  
  1168. static long m_creat(dir,name,mode,attr,entry)
  1169. fcookie *dir;
  1170. char *name;
  1171. unsigned mode;
  1172. int attr;
  1173. fcookie *entry;
  1174. {
  1175.     off_t pos;
  1176.     d_inode ripnew;
  1177.     mino_t newfile;
  1178.     char *ext;
  1179.     pos=0;            /* Keep gcc -Wall happy */
  1180.     
  1181.     /* Create dir entry */    
  1182.     if((pos=search_dir(name,dir->index,dir->dev,ADD))<0)
  1183.     {    
  1184.         return pos;
  1185.     }
  1186.  
  1187.     /* Get new inode */
  1188.     if(!(newfile=alloc_inode(dir->dev)))
  1189.     {
  1190.         DEBUG("m_getdev: no free inodes");
  1191.         return EWRITF;
  1192.     }
  1193.     /* Set up inode */
  1194.     bzero(&ripnew,sizeof(d_inode));
  1195.  
  1196. /* If  creating a file with approriate extensions 
  1197.  * automatically give it execute permissions .
  1198.  */
  1199.     if(do_trans(AEXEC_TOS) && ( ext=strrchr(name,'.') ) )
  1200.     {
  1201.         ext++;
  1202.         if( 
  1203.         /* Insert your favourite extensions here */
  1204.           !(  Stricmp(ext,"TTP") && Stricmp(ext,"PRG") 
  1205.            && Stricmp(ext,"APP") && Stricmp(ext,"TOS") 
  1206.            && Stricmp(ext,"ACC") && Stricmp(ext, "GTP")))
  1207.                 mode |= 0111;
  1208.     }
  1209.     ripnew.i_mode= I_REGULAR | mode;
  1210.     ripnew.i_uid=Getuid();
  1211.     ripnew.i_gid=Getgid();
  1212.     ripnew.i_nlinks=1;
  1213.  
  1214.     ripnew.i_mtime=Unixtime(Timestamp(), Datestamp());
  1215.     ripnew.i_atime=ripnew.i_mtime;
  1216.     ripnew.i_ctime=ripnew.i_mtime;
  1217.  
  1218.     write_inode(newfile,&ripnew,dir->dev);
  1219.     l_write(dir->index,pos,2L,&newfile,dir->dev);
  1220. #ifdef WRITETHRU
  1221.     l_sync();
  1222. #endif
  1223.     entry->fs = dir->fs;
  1224.     entry->dev = dir->dev;
  1225.     entry->index=newfile;
  1226.     entry->aux=0;
  1227.     return 0;
  1228. }
  1229.  
  1230. static DEVDRV * m_getdev(file,special)
  1231. fcookie *file;
  1232. long *special;
  1233. {
  1234.     return(&minix_dev);
  1235. }
  1236.  
  1237. static long m_getxattr(file,xattr)
  1238. fcookie *file;
  1239. XATTR *xattr;
  1240. {
  1241.     off_t inum = file->index;
  1242.     return(istat(inum,file->dev,xattr));
  1243. }
  1244.  
  1245. static long m_chown(file, uid , gid)
  1246. fcookie *file;
  1247. int uid,gid;
  1248. {
  1249.     off_t inum = file->index;
  1250.     return(ichown(inum,file->dev,uid,gid));
  1251. }
  1252.  
  1253. static long m_chmode(file, mode)
  1254. fcookie *file;
  1255. unsigned mode;
  1256. {
  1257.     off_t inum = file->index;
  1258.     return(ichmod(inum,file->dev,mode));
  1259. }
  1260.  
  1261. static long m_mkdir(dir,name,mode)
  1262. fcookie *dir;
  1263. char *name;
  1264. unsigned mode;
  1265. {
  1266.     mino_t newdir;
  1267.     d_inode rip,ripnew;
  1268.     off_t pos;
  1269.     int incr;
  1270.     dir_struct blank[MAX_INCREMENT*2];
  1271.     incr=super_ptr[dir->dev]->increment;
  1272.     if((pos=search_dir(name,dir->index,dir->dev,ADD))<0)return pos;
  1273.     read_inode(dir->index,&rip,dir->dev);
  1274.     if(rip.i_nlinks>=MINIX_MAX_LINK)return EACCDN;
  1275.     /* Get new inode */
  1276.     if(!(newdir=alloc_inode(dir->dev)))return EACCDN;
  1277.  
  1278.     /* Set up inode */
  1279.     bzero(&ripnew,sizeof(d_inode));
  1280.     ripnew.i_mode=I_DIRECTORY | (mode & 0777);
  1281.     ripnew.i_uid=Getuid();
  1282.     ripnew.i_gid=Getgid();
  1283.     ripnew.i_nlinks=2;
  1284.     ripnew.i_mtime=Unixtime(Timestamp(), Datestamp());
  1285.     ripnew.i_ctime=ripnew.i_mtime;
  1286.     ripnew.i_atime=ripnew.i_mtime;
  1287.     write_inode(newdir,&ripnew,dir->dev);
  1288.  
  1289.     /* Set up new directory */
  1290.     strcpy(blank[0].d_name,".");
  1291.     blank[0].d_inum=newdir;
  1292.     strcpy(blank[incr].d_name,"..");
  1293.     blank[incr].d_inum=dir->index;
  1294.     if(l_write((unsigned)newdir,-1L,(off_t)(DIR_ENTRY_SIZE*2*incr),
  1295.         blank,dir->dev)!=(incr*DIR_ENTRY_SIZE*2) )
  1296.     {
  1297.         ripnew.i_mode=0;
  1298.         ripnew.i_nlinks=0;
  1299.         write_inode(newdir,&ripnew,dir->dev);
  1300.         free_inode(newdir,dir->dev);
  1301.         l_sync();
  1302.         return EACCDN;
  1303.     }
  1304.     rip.i_nlinks++;
  1305.     write_inode(dir->index,&rip,dir->dev);
  1306.     l_write(dir->index,pos,2L,&newdir,dir->dev);
  1307. #ifdef WRITETHRU
  1308.     l_sync();
  1309. #endif
  1310.     return(0);
  1311. }
  1312.  
  1313. static long m_rmdir(dir,name)
  1314. fcookie *dir;
  1315. char *name;
  1316. {
  1317.     long chunk,left;
  1318.     long inum;
  1319.     int i,incr;
  1320.     d_inode rip,rip2;
  1321.     if((inum=search_dir(name,dir->index,dir->dev,FIND))<0)return inum;
  1322.     read_inode(inum,&rip,dir->dev);
  1323.     read_inode(dir->index,&rip2,dir->dev);
  1324.     if(!IS_DIR(rip))return EFILNF;
  1325.     incr=super_ptr[dir->dev]->increment;
  1326.     /* Check if dir is actually empty */
  1327.     for(chunk=0;(left=next_zone(&rip,chunk,&temp,dir->dev)/DIR_ENTRY_SIZE);
  1328.         chunk++)
  1329.     {
  1330.         for(i=0;i<left;i+=incr)
  1331.            if(temp.bdir[i].d_inum && strcmp(".",temp.bdir[i].d_name) 
  1332.             && strcmp("..",temp.bdir[i].d_name))
  1333.             return EACCDN ;
  1334.     }
  1335.     if(!inode_busy(inum,dir->dev))
  1336.     {
  1337.         trunc_inode(&rip,dir->dev,0L,0);
  1338.         rip.i_mode=0;
  1339.         free_inode(inum,dir->dev);
  1340.     }
  1341.     rip.i_nlinks=0;
  1342.     write_inode(inum,&rip,dir->dev);
  1343.     read_inode(dir->index,&rip,dir->dev);
  1344.     rip.i_mtime=Unixtime(Timestamp(), Datestamp());
  1345.     rip.i_nlinks--;
  1346.     write_inode(dir->index,&rip,dir->dev);
  1347.     search_dir(name,dir->index,dir->dev,KILL);
  1348. #ifdef WRITETHRU
  1349.     l_sync();
  1350. #endif
  1351.     return(0);
  1352. }
  1353.  
  1354. /* Unix-like unlink ... only works on regular files and symlinks but it should
  1355.  * be safe to unlink an open file
  1356.  */
  1357.  
  1358. static long m_remove(dir,name)
  1359. fcookie *dir;
  1360. char *name;
  1361. {
  1362.     long inum,ret;
  1363.     d_inode rip;
  1364.     inum=search_dir(name,dir->index,dir->dev,FIND);
  1365.     if(inum<0) return inum;
  1366.     read_inode(inum,&rip,dir->dev);
  1367.     if(!IS_REG(rip) && ((rip.i_mode & I_TYPE) != I_SYMLINK) ) return EACCDN;
  1368.  
  1369.     if((ret=search_dir(name,dir->index,dir->dev,KILL))<0) return ret;    
  1370.     if(--rip.i_nlinks==0)
  1371.     {
  1372.         if(!inode_busy(inum,dir->dev))    /* Is inode busy ? */
  1373.         {
  1374.             trunc_inode(&rip,dir->dev,0L,0);
  1375.             rip.i_mode=0;
  1376.             free_inode(inum,dir->dev);
  1377.         }
  1378.     }
  1379.     write_inode(inum,&rip,dir->dev);
  1380. #ifdef WRITETHRU
  1381.     l_sync();
  1382. #endif
  1383.     return(0);
  1384. }
  1385.  
  1386. /* This function is inefficient , it uses the standard sys V method of 
  1387.  * finding out the pathname of the cwd : for each part of the path, search
  1388.  * the parent for a link with the same inode number as '..' , append this to the
  1389.  * path until we get to root dir , then reverse order of dirs. This way no 
  1390.  * temporary buffers are allocated which could overflow or kmalloc to fail ...
  1391.  */
  1392.  
  1393. /* In fact its so inefficient a mini-cache remembers the last call info */
  1394.  
  1395. static long m_getname(root,dir,pathname)
  1396. fcookie *root,*dir;
  1397. char *pathname;
  1398. {
  1399.     mino_t inum,pinum;
  1400.     int chunk;
  1401.     long left;
  1402.     int incr;
  1403.     if(lpath && lroot.dev==root->dev && 
  1404.         lroot.index==root->index && ldir.dev==dir->dev && 
  1405.         ldir.index==dir->index)
  1406.     {
  1407.         TRACE("m_getname: cache hit");
  1408.         strcpy(pathname,lpath);
  1409.         return 0;
  1410.     }
  1411.     *pathname=0;
  1412.     incr=super_ptr[dir->dev]->increment;
  1413.     inum=dir->index;
  1414.     while(inum!=root->index && inum!=ROOT_INODE)
  1415.     {
  1416.         d_inode rip;
  1417.         bufr *tmp;
  1418.         pinum=search_dir("..",inum,dir->dev,FIND); 
  1419.         /* Parent inum */
  1420.             
  1421.         if(pinum < 0) /* If this happens we're in trouble */
  1422.         {
  1423.             ALERT("No .. in inode %d , drive %c",inum,dir->dev+'A');
  1424.             return pinum;
  1425.         }
  1426.         read_inode(pinum,&rip,dir->dev);
  1427.         for(chunk=0;
  1428.         (left=cnext_zone(&rip,chunk,&tmp,dir->dev)/DIR_ENTRY_SIZE) &&
  1429.         inum!=pinum ;chunk++)
  1430.         {
  1431.             char tname[MNAME_MAX+1];
  1432.             int i;
  1433.             for(i=0;i<left && inum!=pinum ;i+=incr)
  1434.             if(tmp->bdir[i].d_inum==inum)
  1435.             {
  1436.                 strncpy(tname,tmp->bdir[i].d_name,MMAX_FNAME(incr));
  1437.                 tname[MMAX_FNAME(incr)]=0;
  1438.                 strrev(tname);
  1439.                 strcat(pathname,tname);
  1440.                 strcat(pathname,"\\");
  1441.                 inum=pinum;
  1442.             }
  1443.         }
  1444.         if(left==0 && inum!=pinum) {
  1445.             ALERT("m_getname inode %d orphaned or bad ..",inum);
  1446.             return EINTRN;
  1447.         }
  1448.     }
  1449.     if(inum==ROOT_INODE && root->index!=ROOT_INODE)
  1450.     {
  1451.         DEBUG("m_getname: Hmmmm root is not a parent of dir");
  1452.         return EINTRN;
  1453.     }
  1454.     strrev(pathname);
  1455.     if(lpath)Kfree(lpath);
  1456.     if(lpath=Kmalloc(strlen(pathname)+1))strcpy(lpath,pathname);
  1457.     lroot=*root;
  1458.     ldir=*dir;
  1459.     return 0;
  1460. }
  1461.  
  1462. /* Minix fs device driver */
  1463.  
  1464. /* Under minixfs there is no 'per file structure' , that is all references to
  1465. the same file are independent.This complicates file sharing a bit , the 'next'
  1466. field points to the next fileptr for the minixfs , so that a search checks the
  1467. list sequentially (the global variable 'firstptr' is the start of the list) ,
  1468. references to the same file are grouped together so that the first reference
  1469. can act as a list pointer to denyshare() , though the last reference's 'next'
  1470. pointer is temporarily set to NULL to keep denyshare() happy. For file locking
  1471. the 'devinfo' field points to the first LOCK structure for this file (if any).
  1472. */
  1473.  
  1474. static long m_open(f)
  1475. FILEPTR *f;
  1476. {
  1477.     FILEPTR *lst,*elst,*tmplst;
  1478.     d_inode rip;
  1479.  
  1480.     /* do some sanity checking */
  1481.     read_inode(f->fc.index,&rip,f->fc.dev);
  1482.  
  1483. /* maybe we should allow read access to directories, but since no other
  1484.  * MiNT file systems do, it doesn't really seem necessary -- ERS
  1485.  */
  1486.     if (!IS_REG(rip)) {
  1487.         DEBUG("m_open: not a regular file");
  1488.         return EACCDN;
  1489.     }
  1490.  
  1491.     /* should we truncate the file? */
  1492.     if (f->flags & O_TRUNC) {
  1493.         trunc_inode(&rip,f->fc.dev,0L,1);
  1494.         rip.i_size = 0;
  1495.         rip.i_mtime = Unixtime(Timestamp(), Datestamp());
  1496.         write_inode(f->fc.index,&rip,f->fc.dev);
  1497. #ifdef WRITETHRU
  1498.         l_sync();
  1499. #endif
  1500.     }
  1501.  
  1502.     /* Find first pointer to same file , if any */
  1503.     for(lst=firstptr;lst;lst=lst->next)
  1504.         if((f->fc.dev==lst->fc.dev) && (f->fc.index==lst->fc.index))break;
  1505.     if(!lst) /* Not found */
  1506.     {
  1507.         /* Stick new fptr at top */
  1508.         f->next=firstptr;
  1509.         firstptr=f;
  1510.         f->devinfo=0;
  1511.         return 0;
  1512.     }
  1513.     /* Find last pointer to file */
  1514.     for(elst=lst;elst->next;elst=elst->next)
  1515.     if((elst->next->fc.dev!=lst->fc.dev) || 
  1516.         (elst->next->fc.index!=lst->fc.index))break;
  1517.     
  1518.     tmplst=elst->next;
  1519.     elst->next=0;
  1520.  
  1521.     if(Denyshare(lst,f))
  1522.     {
  1523.         elst->next=tmplst;
  1524.         return EACCDN;
  1525.     }
  1526.     elst->next=f;
  1527.     f->next=tmplst;
  1528.     f->devinfo=elst->devinfo;
  1529.  
  1530.     return 0;
  1531. }
  1532.  
  1533. static long
  1534. m_close(f, pid)
  1535.     FILEPTR *f;
  1536.     int pid;
  1537. {
  1538.     FILEPTR *last;
  1539.  
  1540.     /* If locked remove any locks for this pid */
  1541.     if(f->flags & O_LOCK)
  1542.     {
  1543.         LOCK *lck,**oldl,*tdev;
  1544.         TRACE("minixfs: removing locks for pid %d",pid);
  1545.         tdev=(LOCK *)f->devinfo;
  1546.         oldl=&tdev;
  1547.         lck=tdev;
  1548.         while(lck)
  1549.         {
  1550.             if(lck->l.l_pid == pid) 
  1551.             {
  1552.                 *oldl=lck->next;
  1553.                 Kfree(lck);
  1554.             }
  1555.             else oldl = &lck->next;
  1556.  
  1557.             lck = *oldl;
  1558.         }
  1559.  
  1560.         if(((long)tdev)!=f->devinfo)    /* First member changed */
  1561.         {
  1562.             FILEPTR *fp;
  1563.             char found=0;
  1564. TRACE("m_close resetting devinfo");
  1565.             for(fp=firstptr;fp;fp=fp->next)
  1566.             {
  1567.                if(fp->fc.index==f->fc.index &&
  1568.                 fp->fc.dev==f->fc.dev)
  1569.                 {
  1570.                     fp->devinfo=(long)tdev;
  1571.                     found=1;
  1572.                 }
  1573.                 else if(found) break;
  1574.             }
  1575.         }
  1576.     }
  1577.  
  1578.     if (f->links <= 0) {
  1579.         if(f->fc.aux & AUX_DEL) /* Marked for deletion ? */
  1580.         {
  1581.             d_inode rip;
  1582.             if(inode_busy(f->fc.index,f->fc.dev)!=2)
  1583.             {
  1584.                 read_inode(f->fc.index,&rip,f->fc.dev);
  1585.                 trunc_inode(&rip, f->fc.dev,0L,0);
  1586.                 rip.i_mode=0;
  1587.                 write_inode(f->fc.index,&rip,f->fc.dev);
  1588.                 free_inode(f->fc.index,f->fc.dev);
  1589.             }
  1590.         }
  1591.         if(f==firstptr) firstptr=f->next;
  1592.         else
  1593.         {
  1594.             for(last=firstptr;last && (last->next!=f);last=last->next);
  1595.             if(!last)ALERT("Minixfs FILEPTR chain corruption!");
  1596.             else last->next=f->next;
  1597.         }
  1598.     }
  1599.     l_sync();    /* always sync on close */
  1600. TRACE("m_close done");
  1601.     return 0;
  1602. }
  1603.  
  1604. /* Minix read , all manner of horrible things can happen during 
  1605.  * a read , if fptr->pos is not block aligned we need to copy a partial
  1606.  * block then a load of full blocks then a final possibly partial block
  1607.  * any of these can hit EOF and we mustn't copy anything past EOF ...
  1608.  * my poor head :-(
  1609.  */
  1610.  
  1611. static long 
  1612. m_read(f,buf,len)
  1613. FILEPTR *f;
  1614. char *buf;
  1615. long len;
  1616. {
  1617.     register void *p=buf;
  1618.     d_inode rip;
  1619.     zone_nr chunk,znew;
  1620.     off_t left=len;
  1621.     cache_control *control;    
  1622.     super_info *psblk;
  1623.     assert(f);
  1624.     assert(f->dev == &minix_dev);
  1625.     psblk=super_ptr[f->fc.dev];
  1626.     chunk=f->pos/BLOCK_SIZE;
  1627.     read_inode(f->fc.index,&rip,f->fc.dev);
  1628.     control= IS_DIR(rip) ? &syscache : &usrcache;
  1629.     /* Are we at EOF ? */
  1630.     if(f->pos>=rip.i_size)
  1631.         return(0);
  1632.  
  1633.     while(left) {
  1634.         off_t c_left,cnum;
  1635.         znew=find_zone(&rip,chunk,f->fc.dev,0);
  1636.     /* Valid characters in current block */
  1637.         c_left=min(rip.i_size-chunk*(long)BLOCK_SIZE,BLOCK_SIZE)
  1638.             -(f->pos & (BLOCK_SIZE-1));
  1639.         if(c_left<=0)break;
  1640.         cnum=min(c_left,left);
  1641.         chunk++;
  1642.         if(cnum==BLOCK_SIZE)read_zones(znew,p,f->fc.dev,control);
  1643.         else
  1644.         {
  1645.             read_zone(znew,&temp,f->fc.dev,control);
  1646.             bcopy(&temp.bdata[f->pos & (BLOCK_SIZE-1)],p,cnum);
  1647.         }
  1648.         p+=cnum;
  1649.         f->pos+=cnum;
  1650.         left-=cnum;
  1651.     }
  1652.     read_zones(0,NULL,-1,control);
  1653.  
  1654. /* Normally we dont care about setting atime , since the writing of
  1655.  * inodes will simply ignore the extra fields for V1 filesystems ,
  1656.  * however read doesn't usually write inodes so we check if its V2
  1657.  * since its silly writing to a V1 filesystem with the modified access
  1658.  * time because it wont be stored ! Also for floppies never update atime
  1659.  * this is a bit of a hack , should really test write protection and act
  1660.  * accordingly. Also note l_sync() is not called here , it's hardly the
  1661.  * end of the world if atime is not updated because of a crash and it
  1662.  * improves performance a bit by delaying the update until close().
  1663.  */
  1664.  
  1665.     if(psblk->version && (psblk->dev > 1)) 
  1666.     {
  1667.         rip.i_atime=Unixtime(Timestamp(),Datestamp());
  1668.         write_inode(f->fc.index,&rip,f->fc.dev);
  1669.     }
  1670.     return(len-left);
  1671. }
  1672.  
  1673. /* seek is a bit easier */
  1674.  
  1675. static long m_seek(f,offset,flag)
  1676. FILEPTR *f;
  1677. long offset;
  1678. int flag;
  1679. {
  1680.     d_inode rip;
  1681. #ifdef ROBUST
  1682.     long oldpos;
  1683.     oldpos=f->pos;
  1684. #endif
  1685.     read_inode(f->fc.index,&rip,f->fc.dev);
  1686.     switch(flag) {
  1687.     case SEEK_SET :
  1688.         if( offset >= 0 ) f->pos=offset;
  1689.         else return(ERANGE);
  1690.         break;
  1691.  
  1692.     case SEEK_CUR :
  1693.         if(f->pos+offset >= 0 ) f->pos+=offset;
  1694.         else return(ERANGE);
  1695.         break;
  1696.  
  1697.     case SEEK_END :
  1698.         if(rip.i_size >= -offset ) f->pos=rip.i_size + offset; 
  1699.         else return(ERANGE);
  1700.         break;
  1701.  
  1702.     default :
  1703.         return(EINVFN);
  1704.  
  1705.     }
  1706. #ifdef ROBUST
  1707.     if(oldpos/BLOCK_SIZE!=f->pos/BLOCK_SIZE) f->fc.aux|=AUX_SYNC;
  1708. #endif
  1709.     return (f->pos);
  1710. }
  1711.  
  1712. static long m_write(f,buf,len)
  1713. FILEPTR *f;
  1714. char *buf;
  1715. long len;
  1716. {
  1717.     long ret;
  1718.     assert(f);
  1719.     assert(f->dev == &minix_dev);
  1720.  
  1721.     ret=l_write(f->fc.index,f->pos,len,buf,f->fc.dev);
  1722.  
  1723.     if(ret<0) return ret;
  1724.  
  1725. #ifdef ROBUST
  1726.     /* This checks if m_seek has scheduled a sync or if the write has
  1727.      * crossed a block boundary , if so sync the filesystem
  1728.      */
  1729.     if( (f->fc.aux & AUX_SYNC) || 
  1730.         ( ( (f->pos & (BLOCK_SIZE-1))+ret) >=BLOCK_SIZE) )
  1731.     {
  1732.         l_sync();
  1733.         f->fc.aux &=~AUX_SYNC;
  1734.     }
  1735. #endif
  1736.     f->pos+=ret;
  1737.  
  1738.     return(ret);
  1739. }
  1740.  
  1741. static long m_ioctl(f,mode,buf)
  1742. FILEPTR *f;
  1743. int mode;
  1744. void *buf;
  1745. {
  1746.     if((mode==FIONREAD) || (mode==FIONWRITE)) 
  1747.     {
  1748.         *((long *) buf)=1;
  1749.         return 0;
  1750.     }
  1751.  
  1752. /* File locking code , as with sharing a bit of care is needed since no
  1753.  * per file structure exists , what we do is use the 'devinfo' field of
  1754.  * a pointer to a linked list of locks , care is needed because if the
  1755.  * first lock vanishes then we need to correct the devinfo field of all
  1756.  * pointers to this file.
  1757.  */
  1758.  
  1759.     if((mode==F_SETLK) || (mode==F_GETLK) )
  1760.     {
  1761.  
  1762.         LOCK t,*lck,*lastlck;
  1763.  
  1764.         struct flock *fl;
  1765.  
  1766.         int cpid;    /* Current proc pid */
  1767.  
  1768.         if(!lockok)
  1769.         {
  1770.             DEBUG("Locking Not Installed");
  1771.             return EINVFN;
  1772.         }
  1773.  
  1774.         fl= ( struct flock *)buf;
  1775.         t.l=*fl;
  1776.  
  1777.         switch(t.l.l_whence)
  1778.         {
  1779.  
  1780.             case SEEK_SET:
  1781.             break;
  1782.  
  1783.             case SEEK_CUR:
  1784.             t.l.l_start+=f->pos;
  1785.  
  1786.             case SEEK_END:
  1787.             {
  1788.                 d_inode rip;
  1789.                 read_inode(f->fc.index,&rip,f->fc.dev);
  1790.                 t.l.l_start=rip.i_size-t.l.l_start;
  1791.             }
  1792.             break;
  1793.  
  1794.             default:
  1795.             DEBUG("Invalid value for l_whence");
  1796.             return EINVFN;
  1797.         }
  1798.         
  1799.         if(t.l.l_start < 0) t.l.l_start=0;
  1800.         t.l.l_whence=0;
  1801.  
  1802.         if(mode == F_GETLK) {
  1803.             lck = Denylock((LOCK *)f->devinfo,&t);
  1804.             if(lck) *fl = lck->l;
  1805.             else fl->l_type = F_UNLCK;
  1806.             return 0;    
  1807.         }
  1808.  
  1809.         cpid=Getpid();
  1810.  
  1811.         if(t.l.l_type==F_UNLCK)
  1812.         {
  1813.             /* Try to find the lock */
  1814.             lastlck=(LOCK *)f->devinfo;
  1815.             for(lck=lastlck;lck;lck=lck->next)
  1816.              if( lck->l.l_pid == cpid &&
  1817.                  lck->l.l_start == t.l.l_start &&
  1818.                  lck->l.l_len == t.l.l_len)
  1819.                 break;
  1820.              else lastlck=lck;            
  1821.  
  1822.             if(lck) 
  1823.             {
  1824.                 Kfree(lck);
  1825.                 if(lck==lastlck) /* Oooer, devinfo lock freed */
  1826.                 {
  1827.                     FILEPTR *fp;
  1828.                     char found=0;
  1829.  
  1830.                     for(fp=firstptr;fp;fp=fp->next)
  1831.                     {
  1832.                          if(fp->fc.index==f->fc.index &&
  1833.                             fp->fc.dev==f->fc.dev) 
  1834.                         {
  1835.                             f->devinfo=(long)lck->next;
  1836.                             found=1;
  1837.                         }
  1838.                         else if(found) break;
  1839.                     }
  1840.                     if(!found)
  1841.                     {
  1842.                       ALERT("minixfs chain corruption!");
  1843.                         return EINVFN;
  1844.                     }
  1845.                 }
  1846.                 else lastlck->next=lck->next;
  1847.                 return 0;
  1848.             }
  1849.             else return ENSLOCK;
  1850.         }
  1851.  
  1852.         lck=Denylock((LOCK *)f->devinfo,&t);
  1853.  
  1854.         if(lck) return ELOCKED;
  1855.  
  1856.         lck = Kmalloc(SIZEOF(LOCK));
  1857.  
  1858.         if(!lck) return ENSMEM;
  1859.  
  1860.         lck->l = t.l;
  1861.         lck->l.l_pid = cpid;
  1862.  
  1863.         if(f->devinfo)    /* Locks already there ? */
  1864.         {
  1865.             /* Yes, add new lock after first */
  1866.             lck->next=((LOCK *)f->devinfo)->next;
  1867.             ((LOCK *)f->devinfo)->next=lck;
  1868.         }
  1869.         else /* No , initialise all devinfo fields */
  1870.         {
  1871.             FILEPTR *fp;
  1872.             char found=0;
  1873.             lck->next=0;
  1874.             for(fp=firstptr;fp;fp=fp->next)
  1875.             {
  1876.                 if(fp->fc.dev==f->fc.dev &&
  1877.                    fp->fc.index==f->fc.index)
  1878.                 {
  1879.                     found=1;
  1880.                     fp->devinfo=(long)lck;
  1881.                 }
  1882.                 else if(found) break;
  1883.             }
  1884.             if(!found)
  1885.             {
  1886.                 ALERT("minixfs: chain corruption!");
  1887.                 return EINVFN;
  1888.             }
  1889.         }
  1890.         f->flags |=O_LOCK;    /* Lock op done on FILEPTR */
  1891.         return 0;
  1892.     }
  1893.     return EINVFN;
  1894. }
  1895.  
  1896. /* Made this a bit like utimes , sets atime,mtime to give ctime=current time */
  1897.  
  1898. static long m_datime(f,timeptr,flag)
  1899. FILEPTR *f;
  1900. int *timeptr;
  1901. int flag;
  1902. {
  1903.     d_inode rip;
  1904.     super_info *psblk;
  1905.     psblk=super_ptr[f->fc.dev];
  1906.     read_inode(f->fc.index,&rip,f->fc.dev);
  1907.     switch (flag)
  1908.     {
  1909.  
  1910.         case 0 :
  1911.         *((long *)timeptr)=Dostime(_corr(rip.i_mtime));
  1912.         break;
  1913.     
  1914.         case 1 :
  1915.         rip.i_mtime=Unixtime(timeptr[0],timeptr[1]);
  1916.         rip.i_atime=rip.i_mtime;
  1917.         rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  1918.         write_inode(f->fc.index,&rip,f->fc.dev);
  1919. #ifdef WRITETHRU
  1920.         l_sync();
  1921. #endif
  1922.         break;
  1923.  
  1924.         default :
  1925.         return -1;
  1926.         break;
  1927.     }    
  1928.     return 0;
  1929. }
  1930.  
  1931. static long m_select(f,proc,mode)
  1932. FILEPTR *f;
  1933. long proc;
  1934. int mode;
  1935. {
  1936.     return 1;
  1937. }
  1938.  
  1939. static void m_unselect(f,proc,mode)
  1940. FILEPTR *f;
  1941. long proc;
  1942. int mode;
  1943. {
  1944. /* Do nothing */
  1945. }
  1946.  
  1947. static long m_opendir(dirh,flag)
  1948. DIR *dirh;
  1949. int flag;
  1950. {
  1951.         d_inode rip;
  1952.     read_inode(dirh->fc.index,&rip,dirh->fc.dev);
  1953.     dirh->index=0;
  1954.     return 0;
  1955. }
  1956.  
  1957. static long m_readdir(dirh,name,namelen,fc)
  1958. DIR *dirh;
  1959. char *name;
  1960. int namelen;
  1961. fcookie *fc;
  1962. {
  1963.         d_inode rip;
  1964.     bufr *tmp;
  1965.     unsigned entry,chunk;
  1966.     super_info *psblk;
  1967.     off_t limit;
  1968.     int flag,incr;
  1969.     psblk=super_ptr[dirh->fc.dev];
  1970.     if(dirh->flags) flag=do_trans(DIR_TOS);
  1971.     else flag=0;
  1972.     if(!dirh->fc.index)return EACCDN;
  1973.     entry=dirh->index % NR_DIR_ENTRIES;
  1974.     chunk=dirh->index / NR_DIR_ENTRIES;    
  1975.     read_inode(dirh->fc.index,&rip,dirh->fc.dev);
  1976.  
  1977.     /* Not absolutely valid here but near enough.See note in m_read too */
  1978.     if(psblk->version && (dirh->fc.dev > 1) )
  1979.     {
  1980.         rip.i_atime=Unixtime(Timestamp(),Datestamp());
  1981.         write_inode(dirh->fc.index,&rip,dirh->fc.dev);
  1982.     }
  1983.  
  1984.     incr=super_ptr[dirh->fc.dev]->increment;
  1985.  
  1986.     while(limit=cnext_zone(&rip,chunk,&tmp,dirh->fc.dev)/DIR_ENTRY_SIZE)
  1987.     {
  1988.         while( entry < limit)
  1989.           {
  1990.             dir_struct *try=&tmp->bdir[entry];
  1991.             entry+=incr;
  1992.             if(try->d_inum)
  1993.             {
  1994.                 char *tmpnam;
  1995.                 tmpnam=tosify(try->d_name,flag,MMAX_FNAME(incr));
  1996.  
  1997.                 if (dirh->flags==0)
  1998.                 {
  1999.                     namelen -= sizeof(long);
  2000.                     if (namelen <= 0) return ERANGE;
  2001.                     *((long *)name) = (long)try->d_inum;
  2002.                     name += sizeof(long);
  2003.                        }
  2004.  
  2005.                 strncpy(name,tmpnam,namelen);
  2006.                 dirh->index=entry+chunk*NR_DIR_ENTRIES;
  2007.             /* set up a file cookie for this entry */
  2008.                 fc->dev = dirh->fc.dev;
  2009.                 fc->aux = 0;
  2010.                 fc->index = (long)try->d_inum;
  2011.                 fc->fs = &minix_filesys;
  2012.                 if(strlen(tmpnam) >= namelen) 
  2013.                     return ENAMETOOLONG;
  2014.                 return 0;
  2015.             }
  2016.           }
  2017.         if(entry!=NR_DIR_ENTRIES)return ENMFIL;
  2018.         else entry=0;
  2019.         chunk++;
  2020.     }
  2021.     return ENMFIL;
  2022. }
  2023.  
  2024. static long m_rewinddir(dirh)
  2025. DIR *dirh;
  2026. {
  2027.     dirh->index=0;
  2028.     return 0;
  2029. }
  2030.  
  2031. static long m_closedir(dirh)
  2032. DIR *dirh;
  2033. {
  2034.     dirh->fc.index=0;
  2035.     l_sync();
  2036.     return 0;
  2037. }
  2038.  
  2039. static long m_rlabel(dir,name,namelen)
  2040. fcookie *dir;
  2041. char *name;
  2042. int namelen;
  2043. {
  2044.     return EACCDN;
  2045. }
  2046.  
  2047. static long m_wlabel(dir,name)
  2048. fcookie *dir;
  2049. char *name;
  2050. {
  2051.     return EFILNF;
  2052. }
  2053.  
  2054. static long m_dfree(dir,buffer)
  2055. fcookie *dir;
  2056. long *buffer;
  2057. {
  2058.     super_info *psblk ;
  2059.     psblk = super_ptr[dir->dev];
  2060.     buffer[1] = psblk->sblk.s_zones-psblk->sblk.s_firstdatazn;
  2061.     buffer[0] = buffer[1] - count_bits(psblk->zbitmap, 
  2062.                               (ushort)(buffer[1]+1))+1;
  2063.     buffer[2]=512L;
  2064.     buffer[3]=2L;
  2065.     return(0);
  2066. }
  2067.  
  2068. static long m_fscntl(dir,name,cmd,arg)
  2069. fcookie *dir;
  2070. char *name;
  2071. int cmd;
  2072. long arg;
  2073. {
  2074.     FILEPTR *f;
  2075.     mfs_info *inf;
  2076.     super_info *psblk;
  2077.     long inum;
  2078.     int uid,id;
  2079.     d_inode rip;    
  2080.  
  2081.     uid=Getuid();
  2082.  
  2083.     switch(cmd)
  2084.     {
  2085.         case MFS_VERIFY:
  2086.         *((long *)arg)=MFS_MAGIC;
  2087.         return 0;
  2088.  
  2089.         /* Sync the filesystem */
  2090.         case MFS_SYNC:
  2091.         l_sync();
  2092.         return 0;
  2093.  
  2094.         /* Invalidate all cache entries for a given drive */
  2095.         case MFS_CINVALID:
  2096.         if(uid) return EACCDN;
  2097.         m_invalidate(dir->dev);
  2098.         return 0;
  2099.  
  2100.         /* Invalidate all fileptrs for a given drive */
  2101.         case MFS_FINVALID:
  2102.         if(uid) return EACCDN;
  2103.         id=Getpid();
  2104.         for(f=firstptr;f;f=f->next)if(f->fc.dev==dir->dev)m_close(f,id);
  2105.         return 0;
  2106.         
  2107.         case MFS_INFO:
  2108.         psblk=super_ptr[dir->dev];
  2109.         inf=(mfs_info *)arg;
  2110.         inf->total_zones=psblk->sblk.s_zones-psblk->sblk.s_firstdatazn;
  2111.         inf->total_inodes=psblk->sblk.s_ninodes;
  2112.         inf->version=psblk->version+1;
  2113.         inf->increment=psblk->increment;        
  2114.         inf->free_inodes=inf->total_inodes-
  2115.             count_bits(psblk->ibitmap,inf->total_inodes+1)-1;
  2116.         inf->free_zones=inf->total_zones-count_bits(psblk->zbitmap,inf->total_zones+1)+1;
  2117.         return 0;
  2118.  
  2119.         case MFS_IMODE:
  2120.         if(uid) return EACCDN;
  2121.         inum=search_dir(name,dir->index,dir->dev,FIND);
  2122.         if(inum < 0 ) return inum;
  2123.         read_inode(inum,&rip,dir->dev);
  2124.         rip.i_mode=arg;
  2125.         write_inode(inum,&rip,dir->dev);
  2126.  
  2127.         case MFS_GTRANS:
  2128.         *((long *) arg)=trans_mode;
  2129.         return 0;
  2130.  
  2131.         case MFS_STRANS:
  2132.         if(uid) return EACCDN;
  2133.         trans_mode=*((long *)arg);
  2134.         return 0;
  2135.  
  2136.         default:
  2137.         return EINVFN;
  2138.     }
  2139. }
  2140.  
  2141. long count_bits(buf,num)
  2142. ushort *buf;
  2143. long num;
  2144. {
  2145.     long i,count;
  2146.     count = 0;
  2147.     for(i = 0; i < num/16; i++) {
  2148.         if(buf[i]==65535)count+=16;
  2149.         else count+=bitcount(buf[i]);
  2150.     }
  2151.     count += bitcount((ushort)(buf[num/16] & (( 1l<< (num%16)) -1l)));
  2152.     return(count);
  2153. }
  2154.  
  2155. int bitcount(wrd)
  2156. unsigned int wrd;
  2157. {
  2158.     long i;
  2159.     int count;
  2160.  
  2161.     count=0;
  2162.     for(i=1;i<65536;i<<=1)if(wrd & i)count++;    
  2163.     return(count);
  2164. }
  2165.  
  2166.  
  2167. /* I *HATE* this function , it attempts to turn a Minix filename into one
  2168.   which wont cause TOS/DESKTOP etc to blow up , feel free to relace with
  2169.   someting better (I doubt there could be much worse) if flag==0 just return
  2170.   a null terminated version of 'name', mnamlength is the maximum filename length
  2171.  */
  2172.  
  2173. char *tosify(name,flag,mnamlength)
  2174. char *name;
  2175. int flag;
  2176. int mnamlength;
  2177. {
  2178.     static char first[MNAME_MAX+8];
  2179.     char *p,*ldt;
  2180.     static char final[MNAME_MAX+8];
  2181.     if(!strcmp(name,".") || !strcmp(name,".."))return(name);
  2182.  
  2183.     strncpy(first,name,mnamlength);
  2184.     first[mnamlength]=0;
  2185.  
  2186.     if(flag==0)return(first);
  2187.  
  2188.     ldt=strrchr(first,'.');
  2189.     for(p=first;*p;p++)
  2190.         if(islower(*p))
  2191.             *p=toupper(*p);
  2192.         else if(*p=='.')
  2193.             *p=',';
  2194.     if( !ldt || ldt==first || !ldt[1]) {
  2195.         first[8]=0;
  2196.         strcpy(final,first);
  2197.     } else {
  2198.         *ldt=0;
  2199.         ldt[4]=0;
  2200.         strcpy(final,first);
  2201.         final[8]=0;
  2202.         strcat(final,".");
  2203.         strcat(final,ldt+1);
  2204.     }
  2205.     return(final);
  2206. }
  2207.  
  2208.  
  2209. /* Bitmap handling stuff */
  2210.  
  2211. long alloc_zone(drive)
  2212. int drive;
  2213. {
  2214.     long save;
  2215.  
  2216.     super_info *psblk=super_ptr[drive];
  2217.  
  2218.     if( !(save=alloc_bit(psblk->zbitmap,psblk->sblk.s_zones
  2219.             -psblk->sblk.s_firstdatazn+1,psblk->zlast)) )
  2220.         return 0;
  2221.     psblk->zdirty=1; /* Mark zone bitmap as dirty */
  2222.     if(save>psblk->zlast)psblk->zlast=save;
  2223.     return(save+psblk->sblk.s_firstdatazn-1);
  2224. }
  2225.  
  2226. mino_t alloc_inode(drive)
  2227. int drive;
  2228. {
  2229.     ushort save;    
  2230.     super_info *psblk=super_ptr[drive];
  2231.  
  2232.     if(!(save=alloc_bit(psblk->ibitmap,psblk->sblk.s_ninodes+1,0)))
  2233.         return 0;
  2234.     psblk->idirty=1;    /* Mark inode bitmap as dirty */
  2235.     return(save);
  2236. }
  2237.  
  2238. /* Release a zone */
  2239. int free_zone(zone,drive)
  2240. long zone;
  2241. int drive;
  2242. {
  2243.     super_info *psblk=super_ptr[drive];
  2244.     long save,ret;
  2245.     if(chk_zone(zone,drive,"free_zone")) return 1;
  2246.     save=zone+1-psblk->sblk.s_firstdatazn;
  2247.     ret=free_bit(psblk->zbitmap,save);
  2248.     psblk->zdirty=1; /* Mark zone bitmap as dirty */
  2249.     if(save<psblk->zlast)psblk->zlast=save;
  2250.     if(!ret) ALERT("Drive %d zone %ld freeing already free zone !",drive,zone);
  2251.     return(ret);
  2252. }
  2253.  
  2254. /* Release an inode */
  2255. int free_inode(inum,drive)
  2256. unsigned inum;
  2257. int drive;
  2258. {
  2259.     long ret;
  2260.     super_info *psblk=super_ptr[drive];
  2261.     if(chk_inode(inum,psblk,"free_inode")) return 0;
  2262.     ret=free_bit(psblk->ibitmap,inum);
  2263.     psblk->idirty=1;    /* Mark inode bitmap as dirty */
  2264.     if(!ret) ALERT("Drive %d inode %d , freeing already free inode!",drive,inum);
  2265.     return(ret);
  2266. }
  2267.  
  2268.  
  2269. /* This routine is used for allocating both free inodes and free zones 
  2270.  * Search a bitmap for a zero , then return its bit number and change it
  2271.  * to a one ...... but without exceeding 'num' bits 
  2272.  */
  2273.  
  2274. long alloc_bit(buf,num,last)
  2275. ushort *buf;
  2276. long num,last;
  2277. {
  2278.     long i,j,k;
  2279.  
  2280.     k=1;
  2281.     for(i=last>>4;(i<=(num>>4)) && (buf[i]==65535);i++)
  2282.         ;
  2283.     if( i > (num>>4) )
  2284.         return(0);
  2285.     else {
  2286.         for(j=0;j<16;j++) {
  2287.             if(!(buf[i] & k)) {
  2288.                 long free;
  2289.                 free=i*16+j;
  2290.                 if(free>=num)return 0;
  2291.                 buf[i]|=k;
  2292.                 return(free);
  2293.             }
  2294.             k<<=1;
  2295.         }
  2296.     }
  2297.     ALERT("minixfs: alloc_bit: This can't happen !");
  2298.     return 0;
  2299. }
  2300.  
  2301. /* zero a bit of a bitmap return 0 if already zero */
  2302.  
  2303. long free_bit(buf,bitnum)
  2304. ushort *buf;
  2305. long bitnum;
  2306. {
  2307.     register long index=bitnum>>4;
  2308.     register ushort bit = 1 << (bitnum & 15);
  2309.     long ret;
  2310.  
  2311.     ret=buf[index] & bit;
  2312.     buf[index]&= ~bit;
  2313.     return(ret);
  2314. }
  2315.  
  2316. /* l_write is used internally for doing things a normal user cannot such
  2317.  * as writing to a directory ... it accepts 5 parameters , an inode num
  2318.  * a position (current position of write) a count which is the number of
  2319.  * characters to write,a buffer and a drive , it updates i_size as needed 
  2320.  * and allocates zones as required , it is nastier than a read because it 
  2321.  * has to write partial blocks within valid blocks and to write beyond EOF
  2322.  */
  2323.  
  2324. long l_write(inum,pos,len,buf,drive)
  2325. unsigned inum;
  2326. off_t pos;
  2327. off_t len;
  2328. void *buf;
  2329. int drive;
  2330. {
  2331.     register void *p=buf;    /* Current position in buffer */
  2332.     d_inode rip;
  2333.     long chunk;
  2334.     off_t left=len;
  2335.     long zne;
  2336.     cache_control *control;
  2337.     read_inode(inum,&rip,drive);
  2338.      /* Work out which cache to use */
  2339.     control = IS_DIR(rip) ? &syscache : &usrcache;
  2340.     if(pos==-1l) pos=rip.i_size; /* If pos==-1 append */
  2341.     chunk=pos/BLOCK_SIZE;
  2342.  
  2343.     while(left)    /* Loop while characters remain to be written */
  2344.     {
  2345.     off_t zoff;
  2346.     ushort wleft;
  2347.     zne=find_zone(&rip,chunk++,drive,1); /* Current zone in file */
  2348.     if(zne==0)break;            /* Partition full */
  2349.     zoff = pos & (BLOCK_SIZE -1);        /* Current zone position */
  2350.     wleft=min(BLOCK_SIZE-zoff,left);    /*Left to write in curr blk*/    
  2351.     if((zoff) || ( (left < BLOCK_SIZE) && (pos+left<rip.i_size)))
  2352.                     read_zone(zne,&temp,drive,control);
  2353.     else if(wleft!=BLOCK_SIZE)bzero(&temp,(size_t)BLOCK_SIZE);
  2354.     if(wleft!=BLOCK_SIZE)
  2355.     {
  2356.         bcopy(p,&temp.bdata[zoff],(size_t)wleft);
  2357.         write_zone(zne,&temp,drive,control);
  2358.     }
  2359.     else write_zones(zne,p,drive,control);
  2360.     pos+=wleft;
  2361.     p+=wleft;
  2362.     if(pos>rip.i_size)rip.i_size=pos;
  2363.     left-=wleft;
  2364.     }
  2365.     write_zones(0,NULL,-1,control);
  2366.     rip.i_mtime=Unixtime(Timestamp(), Datestamp());
  2367.     write_inode(inum,&rip,drive);            /* Update inode */
  2368.     return(len-left);
  2369. }
  2370.  
  2371. /* inode_busy() scans the list   for a given inode , this is used to stop
  2372.  * unlink blowing away a busy inode. In addition f->aux flag is set this is 
  2373.  * so a subsequent close can recognise that the inode is marked for zapping
  2374.  */
  2375.  
  2376. int inode_busy(inum,drive)
  2377. unsigned inum;
  2378. int drive;
  2379. {
  2380.     FILEPTR *f;
  2381.     int found;
  2382.  
  2383.     found=0;
  2384.     for(f=firstptr;f;f=f->next)
  2385.     {
  2386.     if((f->fc.index==inum) && (f->fc.dev==drive))
  2387.     {
  2388.         f->fc.aux|=AUX_DEL;
  2389.         if(found==1)found=2;
  2390.         else found=1;
  2391.     }
  2392.     else if(found) break; /* Since same files consecutive , halt search */
  2393.     }
  2394.     return (found);
  2395. }
  2396.  
  2397. /* m_rename , move a file or directory . Directories need special attention 
  2398.  * because if /usr/foo is moved to /usr/foo/bar then the filesystem will be
  2399.  * damaged by making the /usr/foo directory inaccessible. The sanity checking
  2400.  * performed is very simple but should cover all cases: Start at the parent
  2401.  * of the destination , check if this is the source inode , if not then 
  2402.  * move back to '..' and check again , repeatedly check until the root inode
  2403.  * is reached , if the source is ever seen on the way back to the root then
  2404.  * the rename is invalid , otherwise it should be OK.
  2405.  */
  2406.  
  2407. static long m_rename(olddir,oldname,newdir,newname)
  2408. fcookie *olddir;
  2409. char *oldname;
  2410. fcookie *newdir;
  2411. char *newname;
  2412. {
  2413.     long finode,ret;
  2414.     d_inode rip;
  2415.     off_t pos;
  2416.     char dirmove,dirren;
  2417.     dirmove=0;
  2418.     dirren=0;
  2419. /* Check cross drives */
  2420.     if(olddir->dev!=newdir->dev)return EXDEV;
  2421.  
  2422. /* Check new doesn't exist and path is otherwise valid */
  2423.     finode=search_dir(newname,newdir->index,newdir->dev,FIND);
  2424.     if(finode>0) return EACCDN;
  2425.     if(finode!=EFILNF) return finode;
  2426.  
  2427. /* Check old path OK */
  2428.     if((finode=search_dir(oldname,olddir->index,olddir->dev,FIND))<0)
  2429.         return finode;
  2430.  
  2431.     read_inode(finode,&rip,olddir->dev);
  2432.  
  2433. /* Sanity check movement of directories */
  2434.     if(IS_DIR(rip))
  2435.     {
  2436.         dirren=1;
  2437.          if(olddir->index!=newdir->index)
  2438.         {
  2439. #ifdef MFS_NMOVE_DIR
  2440.             return EACCDN;
  2441. #else
  2442.             d_inode riptemp;
  2443.             ret=is_parent(newdir->index,finode,olddir->dev);
  2444.             if(ret < 0) return ret;
  2445.             if(ret) return EACCDN;
  2446.             read_inode(newdir->index,&riptemp,newdir->dev);
  2447.             if(riptemp.i_nlinks==MINIX_MAX_LINK) return EACCDN;
  2448.             TRACE("minixfs: valid directory move");
  2449.             dirmove=1;
  2450. #endif
  2451.         }
  2452.     }
  2453.  
  2454.     /* Check the m_getname cache is not invalidated by this move ....
  2455.     if no dir move, invalidate if the ldir is the name being changed ,
  2456.     if we move dir's then if the olddir is a parent of ldir , invalidate */
  2457.  
  2458.     if(ldir.dev==olddir->dev && ( ldir.index==finode ||
  2459.      (dirmove && is_parent(ldir.index,finode,olddir->dev)==1 )))
  2460.     {
  2461.         if(lpath)Kfree(lpath);
  2462.         lpath=0;
  2463.     }
  2464.         
  2465. /* Create new entry */
  2466.       if((pos=search_dir(newname,newdir->index,newdir->dev,ADD))<0) return pos;
  2467. /* Delete old path */
  2468.     if((finode=search_dir(oldname,olddir->index,olddir->dev,KILL))<0)
  2469.             return finode;
  2470.     l_write(newdir->index,pos,2L,&((mino_t)finode)+1,newdir->dev);
  2471.  
  2472. /* When moving directories , fixup things like '..' and nlinks of old and
  2473.  * new dirs
  2474.  */
  2475.  
  2476.     if(dirmove)
  2477.     {
  2478.         pos=search_dir("..",finode,newdir->dev,POS);
  2479.         if(pos<0) 
  2480.         {
  2481.             ALERT("m_rename: no .. in inode %ld",finode);
  2482.             return EACCDN;
  2483.         }
  2484.         if(pos!=DIR_ENTRY_SIZE)
  2485.           ALERT("m_rename: Unexpected .. position in inode %ld",finode);
  2486.         l_write(finode,pos,2L,&((mino_t) newdir->index)+1,newdir->dev);
  2487.         read_inode(olddir->index,&rip,olddir->dev);
  2488.         rip.i_nlinks--;
  2489.         write_inode(olddir->index,&rip,olddir->dev);
  2490.         read_inode(newdir->index,&rip,newdir->dev);
  2491.         rip.i_nlinks++;
  2492.         write_inode(newdir->index,&rip,newdir->dev);
  2493.     }
  2494. #ifdef WRITETHRU
  2495.     l_sync();
  2496. #endif
  2497.  
  2498. /* Check the m_getname cache is not invalidated by this move ....
  2499.  * if no dir alter, invalidate if the ldir is the name being changed ,
  2500.  * if we alter dir's then if the moved dir is a parent of ldir , invalidate.
  2501.  */
  2502.  
  2503.     if(ldir.dev==olddir->dev && ( ldir.index==finode ||
  2504.      (dirren && is_parent(ldir.index,finode,olddir->dev)==1 )))
  2505.     {
  2506.         if(lpath)Kfree(lpath);
  2507.         lpath=0;
  2508.     }
  2509.  
  2510.     return 0;
  2511. }
  2512.  
  2513. /* Return '1' is dir2 is a parent of dir1 , otherwise 0 or negative error 
  2514.  * number 
  2515.  */
  2516.  
  2517. static long is_parent(dir1,dir2,drive)
  2518. unsigned dir1,dir2;
  2519. int drive;
  2520. {
  2521. long itemp=dir1;
  2522. for(;;)
  2523. {
  2524.     if(itemp==dir2)
  2525.     {
  2526.         DEBUG("minixfs: invalid directory move");
  2527.         return 1;
  2528.     }
  2529.     if(itemp==ROOT_INODE)break;
  2530.     itemp=search_dir("..",itemp,drive,FIND);
  2531.     if(itemp < 0)
  2532.     {
  2533.         ALERT("Couldn't trace inode %d back to root",dir1);
  2534.         return EACCDN;
  2535.     }    
  2536. }
  2537. return 0;
  2538. }
  2539.  
  2540. /* Minix hard-link , you can't make a hardlink to a directory ... it causes
  2541.  * too much trouble , use symbolic links instead.
  2542.  */
  2543.  
  2544. static long m_hardlink(fromdir,fromname,todir,toname)
  2545. fcookie *fromdir;
  2546. char *fromname;
  2547. fcookie *todir;
  2548. char *toname;
  2549. {
  2550.     long finode;
  2551.     d_inode rip;
  2552.     off_t pos;
  2553.  
  2554. /* Check cross drives */
  2555.     if(fromdir->dev!=todir->dev)return EXDEV;
  2556.  
  2557. /* Check new doesn't exist and path is otherwise valid */
  2558.     finode=search_dir(toname,todir->index,todir->dev,FIND);
  2559.     if(finode>0) return EACCDN;
  2560.     if(finode!=EFILNF) return finode;
  2561.  
  2562. /* Check old path OK */
  2563.     if((finode=search_dir(fromname,fromdir->index,fromdir->dev,FIND))<0)
  2564.         return finode;
  2565.  
  2566.     read_inode(finode,&rip,fromdir->dev);
  2567.     if(!IS_REG(rip) || (rip.i_nlinks >=MINIX_MAX_LINK) ) return EACCDN;
  2568.  
  2569. /* Create new entry */
  2570.     if((pos=search_dir(toname,todir->index,todir->dev,ADD))<0) return pos;
  2571.     l_write(todir->index,pos,2L,&((mino_t)finode)+1,todir->dev);    
  2572.     rip.i_nlinks++;
  2573.     rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  2574.     write_inode(finode,&rip,fromdir->dev);
  2575. #ifdef WRITETHRU
  2576.     l_sync();
  2577. #endif
  2578.     return 0;
  2579. }
  2580.  
  2581. /* Symbolic links ... basically similar to a regular file with one zone */
  2582.  
  2583. static long m_symlink(dir,name,to)
  2584. fcookie *dir;
  2585. char *name;
  2586. char *to;
  2587. {
  2588.     d_inode rip;
  2589.     off_t pos;
  2590.     mino_t newinode;
  2591.  
  2592.     if(!*to)
  2593.     {
  2594.         DEBUG("m_symlink: invalid null filename");
  2595.         return EACCDN;
  2596.     }
  2597.  
  2598.     if(strlen(to)>=SYMLINK_NAME_MAX)
  2599.     {
  2600.         DEBUG("minixfs: Symbolic link name too long");        
  2601.         return ERANGE;
  2602.     }
  2603.  
  2604.     if((pos=search_dir(name,dir->index,dir->dev,ADD))<0) return pos;
  2605.  
  2606.     if(!(newinode=alloc_inode(dir->dev)))
  2607.     {
  2608.         DEBUG("minixfs: symlink drive %c,no free inodes",dir->dev+'A');
  2609.         return EACCDN;
  2610.     }
  2611.     
  2612.  
  2613.     bzero(&rip,sizeof(d_inode));
  2614.     rip.i_mode=I_SYMLINK | 0777;
  2615.     rip.i_size=strlen(to)+1;
  2616.     rip.i_uid=Getuid();
  2617.     rip.i_gid=Getgid();
  2618.     rip.i_mtime=Unixtime(Timestamp(),Datestamp());
  2619.     rip.i_ctime=rip.i_mtime;
  2620.     rip.i_atime=rip.i_mtime;
  2621.     rip.i_nlinks=1;
  2622.  
  2623.     if(!(rip.i_zone[0]=alloc_zone(dir->dev)))
  2624.     {
  2625.         free_inode(newinode,dir->dev);
  2626.         DEBUG("minixfs: symlink drive %c no free zones",dir->dev+'A');
  2627.         return EACCDN;
  2628.     }
  2629.     btos_cpy((char *)&temp,to);
  2630.      write_zone(rip.i_zone[0],&temp,dir->dev,&syscache);
  2631.     write_inode(newinode,&rip,dir->dev);
  2632.     l_write(dir->index,pos,2L,&newinode,dir->dev);
  2633. #ifdef WRITETHRU
  2634.     l_sync();
  2635. #endif
  2636.     return 0;
  2637. }
  2638.  
  2639. static long m_readlink(file,buf,len)
  2640. fcookie *file;
  2641. char *buf;
  2642. int len;
  2643. {
  2644.     off_t inum = file->index;
  2645.     d_inode rip;
  2646.  
  2647.     read_inode(inum,&rip,file->dev);
  2648.     if( (rip.i_mode & I_TYPE)!=I_SYMLINK)
  2649.     {
  2650.         DEBUG("minixfs: attempted readlink on non-symlink");
  2651.         return EACCDN;
  2652.     }
  2653.     read_zone(rip.i_zone[0],&temp,file->dev,&syscache);
  2654.     if(stob_ncpy(buf, (char *) &temp,len))
  2655.     {
  2656.         DEBUG("m_readlink: name too long");
  2657.         return ERANGE;
  2658.     }
  2659.     TRACE("m_readlink returned %s",buf);
  2660.  
  2661.     return 0;
  2662. }
  2663.  
  2664. /* Copy characters from 'from' to 'to' translating backslashes to slashes */
  2665.  
  2666. void btos_cpy(to,from)
  2667. char *to,*from;
  2668. {
  2669.     char c;
  2670.     do {
  2671.         c=*from++;    
  2672.         if(c=='\\')*to++='/';
  2673.         else *to++=c;
  2674.     }
  2675.     while(c);
  2676. }
  2677.  
  2678. /* Translate slashes to backslashes , return zero if all characters copied */
  2679.  
  2680. int stob_ncpy(to,from,n)
  2681. char *to,*from;
  2682. long n;
  2683. {
  2684.     char c;
  2685.  
  2686.     if(n==0) return 0;
  2687.  
  2688.     do
  2689.     {
  2690.         c=*from++;
  2691.         if(c=='/')*to++='\\';
  2692.         else *to++=c;
  2693.     }
  2694.     while(--n && c );
  2695.  
  2696.     if(c)
  2697.     {
  2698.         *--to='\0';
  2699.         return 1;
  2700.     }
  2701.     return 0;
  2702. }
  2703.  
  2704.  
  2705. /* the only settable attribute is FA_RDONLY; if the bit is set,
  2706.  * the mode is changed so that no write permission exists
  2707.  */
  2708. static long m_chattr(file,attr)
  2709. fcookie *file;
  2710. int attr;
  2711. {
  2712.         off_t inum = file->index;
  2713.     int drive = file->dev;
  2714.     d_inode rip;
  2715.  
  2716.     if ( (attr & FA_RDONLY) ) {
  2717.         read_inode(inum,&rip,drive);
  2718.         rip.i_mode &= ~(0222);    /* turn off write permission */
  2719.         rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  2720.         write_inode(inum,&rip,drive);
  2721.         l_sync();
  2722.     } else if (attr == 0) {
  2723.         read_inode(inum,&rip,drive);
  2724.         if ( (rip.i_mode & 0222) == 0 ) {
  2725.             rip.i_mode |= ( (rip.i_mode&0444) >> 1 );
  2726.                 /* turn write permission back on */
  2727.             rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  2728.             write_inode(inum,&rip,drive);
  2729.             l_sync();
  2730.         }
  2731.     }
  2732.     return 0;
  2733. }
  2734.  
  2735. static long m_pathconf(dir,which)
  2736. fcookie *dir;
  2737. int which;
  2738. {
  2739.     switch(which) {
  2740.     case -1:
  2741.         return DP_MAXREQ;
  2742.     case DP_IOPEN:
  2743.         return UNLIMITED;
  2744.     case DP_MAXLINKS:
  2745.          return MINIX_MAX_LINK;
  2746.     case DP_PATHMAX:
  2747.         return PATH_MAX; 
  2748.             /*(although there is no restriction on minixfs itself)*/
  2749.     case DP_NAMEMAX:
  2750.         return MMAX_FNAME(super_ptr[dir->dev]->increment);
  2751.     case DP_ATOMIC:
  2752.         return BLOCK_SIZE;    /* we can write at least a block atomically */
  2753.     case DP_TRUNC:
  2754.         return DP_AUTOTRUNC;
  2755.     default:
  2756.         return EINVFN;
  2757.     }
  2758. }
  2759.  
  2760. /* Decide whether to translate or not according to 'flag' this assumes 'flag'
  2761.  * is the tos domain form and flag<<1 is the mint domain form. This code has
  2762.  * been written so Domain() is called at most once .
  2763.  */
  2764.  
  2765. int do_trans(flag)
  2766. long flag;
  2767. {
  2768.     long tmode,mmode;
  2769.     tmode = trans_mode & flag ;
  2770.     mmode = trans_mode & (flag<<1) ;
  2771.  
  2772.     /* If both modes , always translate */
  2773.     if(mmode && tmode) return 1;
  2774.     /* If neither , never */
  2775.     if(!mmode && !tmode) return 0;
  2776.     
  2777.     if(mmode) 
  2778.     {
  2779.         if (Domain()==DOM_MINT) return 1;
  2780.         else return 0;
  2781.     }
  2782.     if( tmode && (Domain()==DOM_TOS) ) return 1;
  2783.     return 0;
  2784. }
  2785.  
  2786. /* Truncate an inode to 'count' zones, this is used by unlink() as well as
  2787.  * (f)truncate() . Bit tricky this , we have to note which blocks to free,
  2788.  * and free indirection/double indirection blocks too but iff all the blocks
  2789.  * inside them are free too. We also need to keep count of how much to leave 
  2790.  * alone , sparse files complicate this a bit .... so do 2 fs versions ....
  2791.  */
  2792.  
  2793. void trunc_inode(rip,drive,count,zap)
  2794. d_inode *rip;        
  2795. int drive;
  2796. long count;            /* number of blocks to leave */
  2797. int zap;            /* flag to alter inode */
  2798. {
  2799.     int i,j;
  2800.     bufr *tmp;
  2801.     char some,dirty;
  2802.     super_info *psblk=super_ptr[drive];
  2803.     char vers;
  2804.     vers=psblk->version;
  2805.     /* Handle zones in inode first */
  2806.     if(count<psblk->dzpi)
  2807.     {
  2808.         for(i=count;i<psblk->dzpi;i++) {
  2809.             if(rip->i_zone[i])
  2810.                 free_zone(rip->i_zone[i],drive);
  2811.             if(zap)rip->i_zone[i]=0;
  2812.         }
  2813.         count=0;
  2814.     }
  2815.     else count-=psblk->dzpi;
  2816. /* Handle indirect zone */
  2817.     if(count< psblk->zpind) {
  2818.         some=0;
  2819.         dirty=0;
  2820.         if(rip->i_zone[7]) {
  2821.             tmp=get_zone(rip->i_zone[7],drive);
  2822.             for(i=0;i<psblk->zpind;i++) {
  2823.                 if(PIND(vers,tmp,i)) {
  2824.                     if(count)some=1;
  2825.                     else {
  2826.                         free_zone(PIND(vers,tmp,i),drive);
  2827.                         if(zap)PIND(vers,tmp,i)=0;
  2828.                         dirty=1;
  2829.                     }
  2830.                 }    
  2831.                 if(count)count--;
  2832.             }
  2833.             if(!some) {
  2834.                 free_zone(rip->i_zone[7],drive);
  2835.                 if(zap)rip->i_zone[7]=0;
  2836.             }
  2837.             else if(dirty) 
  2838.                 write_zone(rip->i_zone[7],tmp,drive,&syscache);
  2839.         }
  2840.     }
  2841.     else count-=psblk->zpind;
  2842.     /* Handle double indirect ... */
  2843.     if(rip->i_zone[8]) {
  2844.         some=0;
  2845.         dirty=0;
  2846.         read_zone(rip->i_zone[8],&temp,drive,&syscache);
  2847.         for(i=0;i<psblk->zpind;i++) {
  2848.             if(IND(vers,temp,i)) {
  2849.                 char lsome,ldirty; /* local some,dirty for inds */
  2850.                 lsome=0;
  2851.                 ldirty=0;
  2852.                 tmp=get_zone(IND(vers,temp,i),drive);
  2853.                 for(j=0;j<psblk->zpind;j++) {
  2854.                 if(PIND(vers,tmp,j)) {
  2855.                     if(count) { 
  2856.                         some=1;
  2857.                         lsome=1;
  2858.                     }
  2859.                     else {
  2860.                         free_zone(PIND(vers,tmp,j),drive);
  2861.                         if(zap)PIND(vers,tmp,j)=0;
  2862.                         ldirty=1;
  2863.                         dirty=1;
  2864.                     }
  2865.                 }
  2866.                 if(count)count--;
  2867.                 }
  2868.                 if(!lsome) {
  2869.                 free_zone(IND(vers,temp,i),drive);
  2870.                 if(zap)IND(vers,temp,i)=0;
  2871.                 }
  2872.                 else if(ldirty)
  2873.                 write_zone(IND(vers,temp,i),tmp,drive,&syscache);
  2874.             }
  2875.             else 
  2876.             {
  2877.                 if(count>=psblk->zpind)count-=psblk->zpind;
  2878.                 else count=0;
  2879.             }
  2880.         }
  2881.         if(!some) {
  2882.             free_zone(rip->i_zone[8],drive);
  2883.             if(zap)rip->i_zone[8]=0;
  2884.         }
  2885.         else if(dirty)write_zone(rip->i_zone[8],&temp,drive,&syscache);
  2886.     }
  2887. }
  2888.  
  2889. /* Now for some u*ix like routines */
  2890.  
  2891. /* We will get a load of ixxxx routines which operate on inodes,
  2892.  * the path/fd equivalents can then easily be derived from them
  2893.  * e.g. fstat , stat from istat , chmod , fchmod from ichmod.
  2894.  */
  2895.  
  2896. long istat(inum,drive,st)
  2897. unsigned inum;
  2898. int drive;
  2899. XATTR *st;
  2900. {
  2901.         d_inode rip;
  2902.     off_t time_tmp;
  2903.     super_info *psblk;
  2904.     psblk=super_ptr[drive];
  2905.     read_inode(inum,&rip,drive);
  2906.     /* Minix and gcc use different values for FIFO's */
  2907.     if((rip.i_mode & I_TYPE) == I_NAMED_PIPE)
  2908.         st->attr= S_IFIFO | (rip.i_mode & ALL_MODES);
  2909.         else st->mode=rip.i_mode;
  2910.  
  2911.     /* We could potentially have trouble with symlinks too */
  2912. #if I_SYMLINK != S_IFLNK
  2913.     if( (rip.i_mode & I_TYPE) == I_SYMLINK)
  2914.         st->attr= S_IFLNK | (rip.i_mode & ALL_MODES);
  2915. #endif
  2916.  
  2917.     /* Fake attr field a bit , to keep TOS happy */
  2918.     if(IS_DIR(rip))st->attr=FA_DIR;
  2919.     else st->attr=(rip.i_mode & 0222) ? 0 : FA_RDONLY;
  2920.  
  2921.         st->index=inum;
  2922.         st->dev=drive;
  2923. #if 0
  2924.     /* Char and block special files need major/minor device nos filled in */
  2925.     if(((rip.i_mode & I_TYPE) == I_CHAR_SPECIAL )||
  2926.         ( (rip.i_mode & I_TYPE) == I_BLOCK_SPECIAL ))
  2927.             st->reserved=rip.i_zone[0];
  2928.         else st->reserved=0;
  2929. #endif
  2930.         st->nlink=rip.i_nlinks;
  2931.         st->uid=rip.i_uid;
  2932.         st->gid=rip.i_gid;
  2933.         st->size=rip.i_size;
  2934.     st->blksize = BLOCK_SIZE;
  2935. /* Note: the nblocks calculation is accurate only if the file is
  2936.  * contiguous. It usually will be, and if it's not, it shouldn't
  2937.  * matter ('du' will return values that are slightly too high)
  2938.  */
  2939.     st->nblocks = (st->size + (BLOCK_SIZE-1)) / BLOCK_SIZE;
  2940.     if (st->nblocks >= psblk->dzpi)
  2941.         st->nblocks++;    /* correct for the indirection block */
  2942.     if (st->nblocks > psblk->ndbl) {
  2943.         st->nblocks++;    /* correct for double indirection block */
  2944.         st->nblocks += ((st->nblocks-(psblk->ndbl+2))/psblk->zpind);
  2945.                 /* and single indirection blocks */
  2946.     }
  2947.  
  2948.     time_tmp=Dostime(_corr(rip.i_mtime));
  2949.     st->mtime=time_tmp >> 16;
  2950.     st->mdate=time_tmp & (0xffff);
  2951.     time_tmp=Dostime(_corr(rip.i_atime));
  2952.     st->atime=time_tmp >> 16;
  2953.     st->adate=time_tmp & (0xffff);
  2954.     time_tmp=Dostime(_corr(rip.i_ctime));
  2955.     st->ctime=time_tmp >> 16;
  2956.     st->cdate=time_tmp & (0xffff);
  2957.  
  2958.     return 0;
  2959. }
  2960.  
  2961. long ichmod(inum,drive,mode)
  2962. unsigned inum;
  2963. int drive;
  2964. unsigned mode;
  2965. {
  2966.         d_inode rip;
  2967.     super_info *psblk=super_ptr[drive];
  2968.  
  2969.         read_inode(inum,&rip,drive);
  2970.  
  2971.         rip.i_mode=(rip.i_mode & I_TYPE)|(mode & ALL_MODES);                
  2972.     if(psblk->version)rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  2973.     write_inode(inum,&rip,drive);
  2974.     l_sync();
  2975.         return 0;
  2976. }
  2977.  
  2978. long ichown(inum,drive,user,group)
  2979. unsigned inum;
  2980. int drive;
  2981. int user,group;
  2982. {
  2983.     d_inode rip;
  2984.     read_inode(inum,&rip,drive);
  2985.      if(user!=-1)rip.i_uid=user;
  2986.     if(group!=-1)rip.i_gid=group;
  2987.     rip.i_ctime=Unixtime(Timestamp(),Datestamp());
  2988.     write_inode(inum,&rip,drive);
  2989.     l_sync();
  2990.     return 0;
  2991. }
  2992.  
  2993. long itruncate(inum,drive,length)
  2994. unsigned inum;
  2995. int drive;
  2996. off_t length;
  2997. {
  2998.     off_t count;
  2999.     d_inode rip;
  3000.     read_inode(inum,&rip,drive);
  3001.     /* Regulars only , clever directory compaction stuff later ... */
  3002.     if(!IS_REG(rip))return EACCDN;
  3003.     /* If file smaller than 'length' nothing to do */
  3004.     if(rip.i_size <= length)return 0;
  3005.     count=(length+1023)/1024;
  3006.     trunc_inode(&rip,drive,count,1);
  3007.     rip.i_size=length;    
  3008.     write_inode(inum,&rip,drive);
  3009.     l_sync();
  3010.     return 0;
  3011. }
  3012.